Compare commits

...
16 Commits
Author SHA1 Message Date
Severiano Badajoz 780852fd49 docs: release 0.18.0 (#2413)
* Bump version: 0.17.0 → 0.18.0-rc.0

* Bump version: 0.18.0-rc.0 → 0.18.0
2021-08-31 15:52:30 -07:00
Colin Megill de03129061 Spinner on color by geneset when closed (#2412)
* take 2

* take 2.1

* bruce test

* remove component did mount

* working
2021-08-27 12:15:47 -07:00
Severiano Badajoz 2715dba703 fix: update/remove deprecated packages (#2411)
* replace deprecated packages

* more updates
2021-08-26 15:18:43 -07:00
Bruce Martin 154d099fef X float16 support (#2406)
* float16 support

* fix type checks

* PR review comments

* add tests for custom json encoder; rename and comment for posterity

* lint

* typos
2021-08-25 11:12:49 -07:00
eaae6df5e3 TS Revert (1) (#2402)
* revert all commits to before Typescript migration

* update compat workflow to match latest deps (#2335)

* update compat workflow to match latest deps

* attempt to debug

* attempt to debug

* remove debugging code

* typo

* update deps to match desktop (#2340)

* fix: don't run lint with `--fix` on push tests (#2273)

* fix: don't run lint with `--fix` on push tests

* npx

Co-authored-by: maniarathi <mani.arathi@gmail.com>
Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com>

* rename X_approx_distribution to X_approximate_distribution (#2337)

* Correctly handle non-finite numbers in heuristic determination of X distribution (#2342)

* handle non-finites explicitly

* improve and test edge case handling for distribution estimation

* revert debugging changes

* code readability

* clean up type inferencing (#2332)

* unit tests for 64 bit conversion

* clean up type handling

* type inference tests

* more type inference fixes

* use schema to determine user intent for data typing

* stop using deprecated API

* fbs type encoding test

* add missing test

* add more tests

* correctly infer X type for CXG adaptor

* lint

* fix typo

* ts migration

* cleanup from PR review

* lint

* PR review changes

* remove unused packages from client (#2359)

* remove unused packages from client

* add missing peer dep

* fix: disable FE auth testing on compatibility tests (#2377)

* update: release process (#2277)

Co-authored-by: maniarathi <mani.arathi@gmail.com>

* fix: remove spaces in param setup (#2380)

* delete deploy workflow (#2396)

* undo reformatting which now does not pass lint

* fix snapshots which changed due to npm dep changes

* add missing quoting to snapshot

* another snapshot typo fix

* TS Revert (2) - replay PR #2347 and #2354 (#2403)

* replay edits from PR 2347

* TS Revert (3) - replay edits in PR #2327 (#2404)

* replay edits in PR 2327

* TS Revert (4) - replay PR #2355 (#2405)

* replay edits in PR 2355

* add additional babel config

* reformat with new prettier config

Co-authored-by: Severiano Badajoz <sbadajoz@chanzuckerberg.com>
Co-authored-by: maniarathi <mani.arathi@gmail.com>
Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com>
2021-08-23 15:01:36 -07:00
Madison Dunitz 295590a7c6 delete deploy workflow (#2396) 2021-08-18 17:13:04 -05:00
Mim HastieandTimmy Huang b814489328 Added typings to annoMatrix dir. (#2365) (#2371)
* Added typings to annoMatrix directory. (#2365)

* Added review point updates. (#2365)

* Fixed viewOf defect. Added review point updates. (#2365)

* Updated test to use Field key interpolation. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* Updated test to use Field key interpolation. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* Updated test to use Field key interpolation. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* Updated test to use Field key interpolation. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* Linting. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* Simplified while condition in middleware GC. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* Reverted embedding.ts prettier to resolve conflict. (#2365)

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>
2021-08-18 13:18:54 -07:00
Severiano BadajozandTimmy Huang 08b03ace60 chore: type general utils (#2381)
* type camera

* type reducer store

* type actionhelpers

* type catchErrorsWrap callsite

* missed camera member var

* type nameCreators

* type makeContinousDimensionName callsite

* type promise limit

* type quantile

* type range

* introduce TypedArray + NumericArray

* type range

* cleanup test

* fix call sites

* type plimit call site

* finish typing camera

* use our TypedArray

* type scientific and sigFig utils and callsites

* simple typings

* type catLabelSort

* type callsite

* type

* callsites

* type camera methods

* swap back to strings, set defaults accordingly

* partially type centroid

* explicit tuple and undefined check

* fix references to this

* call constructor with new and casting

* Revert "introduce TypedArray + NumericArray"

This reverts commit cf21538717.

* explicit tuple

* generics and import fixes

* add unsigned 8 clamped arrray

* back to literals

* use arraytypes

* fix return state

* type more actions

* Update client/src/util/actionHelpers.ts

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>

* properly type dispatch

* properly type thunk

* use new dispatch

* remove nullish coallescer

* use AppDispatch

* generic jsonrequest

* use dispatch again

* lint

Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>
2021-08-18 00:15:53 +00:00
Bruce Martin 45cecad76a TS typing for Dataframe (#2382)
* initial TS typing

* first cut at Dataframe TS typing

* more Dataframe typing

* comments

* more Dataframe cleanup

* PR review fixes and improvements
2021-08-17 10:43:40 -07:00
Severiano Badajoz 3fdf5cac9d fix: remove spaces in param setup (#2380) 2021-08-13 17:01:10 +00:00
Severiano Badajozandmaniarathi 3c3a794986 update: release process (#2277)
Co-authored-by: maniarathi <mani.arathi@gmail.com>
2021-08-13 00:25:58 +00:00
Bruce Martin 4b417cb5a5 undoable TS typing (#2374)
* type undoable-related TS

* style change to type declaration
2021-08-12 17:14:09 -07:00
Severiano Badajoz 925b785b1f fix: disable FE auth testing on compatibility tests (#2377) 2021-08-12 18:34:04 +00:00
Bruce Martin 660dff256c add array type foundations (#2376) 2021-08-12 07:17:05 -07:00
Timmy Huang 59c475b821 chore: extract schema types (#2375)
* chore: extract schema types

* address comments
2021-08-12 03:12:21 +00:00
Timmy Huang fc60b2acef fix: thuang-fix-tsconfig-path (#2372)
Thanks so much for the quick review, Bruce!!
2021-08-11 01:38:33 +00:00
273 changed files with 15053 additions and 22621 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.17.0
current_version = 0.18.0
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<prerel>rc)\.(?P<prerelversion>\d+))?
serialize =
@@ -9,6 +9,7 @@ on:
env:
JEST_ENV: prod
CXG_AUTH_TYPE: none
jobs:
docker-build:
-13
View File
@@ -1,13 +0,0 @@
name: Deploy via single cell infra repo
on:
push:
branches: main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: repository dispatch
run: |
curl -XPOST -u czi-sci-single-cell-eng:${{secrets.SCI_GITHUB_TOKEN}} -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/chanzuckerberg/single-cell-infra/dispatches --data '{"event_type": "cellxgene-hook"}'
@@ -1,3 +1,4 @@
from typing import Tuple
import numba
import concurrent.futures
import numpy as np
@@ -6,7 +7,7 @@ from backend.common.constants import XApproximateDistribution
@numba.njit(error_model="numpy", nogil=True)
def min_max(arr: np.ndarray):
def min_max_fast(arr: np.ndarray) -> Tuple[float, float]:
"""Return (min, max) values for the ndarray."""
# initialize to first finite value in array. Normally,
@@ -47,6 +48,24 @@ def min_max(arr: np.ndarray):
return min_val, max_val
def min_max_numpy(arr: np.ndarray) -> Tuple[float, float]:
return arr.min(), arr.max()
def numba_has_support_for_scalar_type(arr: np.ndarray) -> bool:
"""Numba does not support half-floats, 128 bit floats, ints > 64 bit or non-scalars."""
if arr.dtype == np.float32 or arr.dtype == np.float64:
return True
if np.issubdtype(arr.dtype, np.integer) and arr.dtype <= np.int64:
return True
if arr.dtype == np.bool_:
return True
return False
def estimate_approximate_distribution(X) -> XApproximateDistribution:
"""
Estimate the distribution (normal, count) of the X matrix.
@@ -72,6 +91,8 @@ def estimate_approximate_distribution(X) -> XApproximateDistribution:
else:
raise TypeError(f"Unsupported matrix format: {str(type(X))}")
min_max = min_max_fast if numba_has_support_for_scalar_type(Xdata) else min_max_numpy
CHUNKSIZE = 1 << 24
if Xdata.size > CHUNKSIZE:
min_val = max_val = Xdata[0]
+13 -5
View File
@@ -65,7 +65,13 @@ def path_join(base, *urls):
return btpl._replace(path=path).geturl()
class Float32JSONEncoder(json.JSONEncoder):
class StrictJSONEncoder(json.JSONEncoder):
"""
Custom JSON encoder set-up performing two tasks:
1. Strict JSON conformance with non-finite floats (NaN, +/-Inf) via allow_nan=False
2. Convert various Numpy types into python types so the encoder will correctly encode.
"""
def __init__(self, *args, **kwargs):
"""
NaN/Infinities are illegal in standard JSON. Python extends JSON with
@@ -78,9 +84,11 @@ class Float32JSONEncoder(json.JSONEncoder):
super().__init__(*args, **kwargs)
def default(self, obj):
if isinstance(obj, np.float32):
"""This helps us convert types not supported by the native JSON encoder into
standard python types, eg, np.int64."""
if isinstance(obj, np.floating):
return float(obj)
elif isinstance(obj, np.integer):
if isinstance(obj, np.integer):
return int(obj)
return json.JSONEncoder.default(self, obj)
@@ -89,8 +97,8 @@ def custom_format_warning(msg, *args, **kwargs):
return f"[cellxgene] Warning: {msg} \n"
def jsonify_numpy(data):
return json.dumps(data, cls=Float32JSONEncoder, allow_nan=False)
def jsonify_strict(data):
return json.dumps(data, cls=StrictJSONEncoder, allow_nan=False)
def import_plugins(plugin_module):
+2 -2
View File
@@ -24,7 +24,7 @@ import backend.czi_hosted.common.rest as common_rest
from backend.common.utils.data_locator import DataLocator
from backend.common.errors import DatasetAccessError, RequestException
from backend.czi_hosted.common.health import health_check
from backend.common.utils.utils import path_join, Float32JSONEncoder
from backend.common.utils.utils import path_join, StrictJSONEncoder
from backend.czi_hosted.data_common.matrix_loader import MatrixDataLoader
webbp = Blueprint("webapp", "backend.czi_hosted.common.web", template_folder="templates")
@@ -396,7 +396,7 @@ class Server:
self.app = Flask(__name__, static_folder=None)
handle_api_base_url(self.app, app_config)
self._before_adding_routes(self.app, app_config)
self.app.json_encoder = Float32JSONEncoder
self.app.json_encoder = StrictJSONEncoder
server_config = app_config.server_config
if server_config.app__server_timing_headers:
ServerTiming(self.app, force_debug=True)
@@ -15,7 +15,7 @@ from backend.common.errors import (
UnsupportedSummaryMethod,
DatasetAccessError,
)
from backend.common.utils.utils import jsonify_numpy
from backend.common.utils.utils import jsonify_strict
from backend.common.fbs.matrix import encode_matrix_fbs
@@ -336,7 +336,7 @@ class DataAdaptor(metaclass=ABCMeta):
)
try:
return jsonify_numpy(result)
return jsonify_strict(result)
except ValueError:
raise JSONEncodingValueError("Error encoding differential expression to JSON")
+1 -1
View File
@@ -2,7 +2,7 @@ import logging
import sys
from backend.common.utils.utils import import_plugins
__version__ = "0.17.0"
__version__ = "0.18.0"
display_version = "cellxgene v" + __version__
try:
+2 -2
View File
@@ -17,7 +17,7 @@ from flask_restful import Api, Resource
import backend.server.common.rest as common_rest
from backend.common.errors import DatasetAccessError, RequestException
from backend.server.common.health import health_check
from backend.common.utils.utils import Float32JSONEncoder
from backend.common.utils.utils import StrictJSONEncoder
webbp = Blueprint("webapp", "backend.server.common.web", template_folder="templates")
@@ -257,7 +257,7 @@ class Server:
def __init__(self, app_config):
self.app = Flask(__name__, static_folder=None)
self._before_adding_routes(self.app, app_config)
self.app.json_encoder = Float32JSONEncoder
self.app.json_encoder = StrictJSONEncoder
server_config = app_config.server_config
# enable session data
@@ -232,7 +232,7 @@ class AnndataAdaptor(DataAdaptor):
"Anndata data matrix is sparse, but not a CSC (columnar) matrix. "
"Performance may be improved by using CSC."
)
if self.data.X.dtype != "float32":
if self.data.X.dtype > np.dtype(np.float32):
warnings.warn(
f"Anndata data matrix is in {self.data.X.dtype} format not float32. " f"Precision may be truncated."
)
+2 -2
View File
@@ -8,7 +8,7 @@ from server_timing import Timing as ServerTiming
from backend.server.common.config.app_config import AppConfig
from backend.common.constants import Axis, XApproximateDistribution
from backend.common.errors import FilterError, JSONEncodingValueError, ExceedsLimitError, UnsupportedSummaryMethod
from backend.common.utils.utils import jsonify_numpy
from backend.common.utils.utils import jsonify_strict
from backend.common.fbs.matrix import encode_matrix_fbs
from backend.common.genesets import validate_gene_sets
@@ -331,7 +331,7 @@ class DataAdaptor(metaclass=ABCMeta):
)
try:
return jsonify_numpy(result)
return jsonify_strict(result)
except ValueError:
raise JSONEncodingValueError("Error encoding differential expression to JSON")
+1 -1
View File
@@ -18,4 +18,4 @@ geneset_to_delete,,,
geneset_to_edit,,,
fill_this_geneset,,,
empty_this_geneset,,SIK1,
brush_this_gene,,SIK1,
brush_this_gene,,SIK1,
1 # Test fixture
18 geneset_to_edit,,,
19 fill_this_geneset,,,
20 empty_this_geneset,,SIK1,
21 brush_this_gene,,SIK1,
@@ -0,0 +1,58 @@
import unittest
import numpy as np
from backend.common.utils.utils import (
jsonify_strict,
)
class TestJsonifyStrict(unittest.TestCase):
def test_jsonify_numpy_general_cases(self):
self.assertEqual(jsonify_strict({}), "{}")
self.assertEqual(jsonify_strict({"a": [], "b": "hello", "c": True}), '{"a": [], "b": "hello", "c": true}')
def test_jsonify_numpy_float_edges(self):
with self.assertRaises(ValueError):
jsonify_strict({"nan": [np.nan]})
with self.assertRaises(ValueError):
jsonify_strict({"pinf": [np.PINF]})
with self.assertRaises(ValueError):
jsonify_strict({"ninf": [np.NINF]})
def test_jsonify_numpy_ndarray(self):
values = {
"integer": [
np.int8(0),
np.int16(1),
np.int32(2),
np.int64(3),
np.uint8(4),
np.uint16(5),
np.uint32(6),
np.uint64(7),
],
"floating": [
np.float16(100.0),
np.float32(101.0),
np.float64(102.0),
],
}
# these just confirm our test assumptions
self.assertTrue(isinstance(values["floating"][0], np.float16))
self.assertTrue(isinstance(values["floating"][1], np.float32))
self.assertTrue(isinstance(values["floating"][2], np.float64))
self.assertTrue(isinstance(values["integer"][0], np.int8))
self.assertTrue(isinstance(values["integer"][1], np.int16))
self.assertTrue(isinstance(values["integer"][2], np.int32))
self.assertTrue(isinstance(values["integer"][3], np.int64))
self.assertTrue(isinstance(values["integer"][4], np.uint8))
self.assertTrue(isinstance(values["integer"][5], np.uint16))
self.assertTrue(isinstance(values["integer"][6], np.uint32))
self.assertTrue(isinstance(values["integer"][7], np.uint64))
# the actual test!
self.assertEqual(
jsonify_strict(values),
'{"floating": [100.0, 101.0, 102.0], "integer": [0, 1, 2, 3, 4, 5, 6, 7]}',
)
+5 -3
View File
@@ -1,11 +1,13 @@
include ../common.mk
ANNOTATIONS := $(if $(ANNOTATIONS),$(ANNOTATIONS),../backend/test/fixtures/pbmc3k-annotations.csv)
GENE_SETS := $(if $(GENE_SETS),$(GENE_SETS),../backend/test/fixtures/pbmc3k-genesets.csv)
GENE_SETS := $(if $(GENE_SETS),$(GENE_SETS),../backend/test/fixtures/pbmc3k-genesets.csv)
ANNOTATIONS_FILENAME := $(shell basename $(ANNOTATIONS))
GENE_SETS_FILENAME := $(shell basename $(GENE_SETS))
CXG_CONFIG := $(if $(CXG_CONFIG), $(CXG_CONFIG), ./__tests__/e2e/test_config.yaml)
CXG_CONFIG := $(if $(CXG_CONFIG),$(CXG_CONFIG),./__tests__/e2e/test_config.yaml)
CXG_AUTH_TYPE := $(if $(CXG_AUTH_TYPE),$(CXG_AUTH_TYPE),"test")
# Packaging
@@ -38,7 +40,7 @@ smoke-test:
start_server_and_test \
'CXG_OPTIONS="--config-file $(CXG_CONFIG)" $(MAKE) start-server' \
$(CXG_SERVER_PORT) \
'CXG_URL_BASE="http://localhost:$(CXG_SERVER_PORT)" CXG_AUTH_TYPE="test" npm run e2e -- --verbose false'
'CXG_URL_BASE="http://localhost:$(CXG_SERVER_PORT)" CXG_AUTH_TYPE=$(CXG_AUTH_TYPE) npm run e2e -- --verbose false'
# start an instance of cellxgene and run the end-to-end annotations tests
.PHONY: smoke-test-annotations
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`did launch page launched 1`] = `"<span style=\\"max-width: 155px; display: flex; overflow: hidden; justify-content: flex-start; width: 100%; padding: 0px;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">pbm</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">c3k</span><span style=\\"position: absolute; right: 0px; color: inherit;\\">c3k</span></span></span>"`;
exports[`metadata loads categories and values from dataset appear 1`] = `"<div style=\\"display: flex; justify-content: space-between; align-items: baseline;\\"><div style=\\"display: flex; justify-content: flex-start; align-items: flex-start;\\"><label class=\\"bp3-control bp3-checkbox\\" for=\\"category-select-louvain\\"><input id=\\"category-select-louvain\\" data-testclass=\\"category-select\\" data-testid=\\"louvain:category-select\\" type=\\"checkbox\\" checked=\\"\\"><span class=\\"bp3-control-indicator\\"></span></label><span role=\\"menuitem\\" tabindex=\\"0\\" data-testclass=\\"category-expand\\" data-testid=\\"louvain:category-expand\\" style=\\"cursor: pointer;\\"><span aria-haspopup=\\"true\\" class=\\"bp3-popover2-target\\"><span data-testid=\\"louvain:category-label\\" tabindex=\\"-1\\" aria-label=\\"louvain\\" class=\\"\\" style=\\"max-width: 265px;\\"><span style=\\"max-width: 265px; display: flex; overflow: hidden; justify-content: flex-start; width: 100%; padding: 0px;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">lou</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">vain</span><span style=\\"position: absolute; right: 0px; color: inherit;\\">vain</span></span></span></span></span><svg stroke=\\"currentColor\\" fill=\\"currentColor\\" stroke-width=\\"0\\" viewBox=\\"0 0 320 512\\" data-testclass=\\"category-expand-is-not-expanded\\" height=\\"1em\\" width=\\"1em\\" xmlns=\\"http://www.w3.org/2000/svg\\" style=\\"font-size: 10px; margin-left: 5px;\\"><path d=\\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\\"></path></svg></span></div><div><span class=\\"bp3-popover-wrapper\\"><span aria-haspopup=\\"true\\" class=\\"bp3-popover-target\\"><a role=\\"button\\" data-testclass=\\"colorby\\" data-testid=\\"colorby-louvain\\" class=\\"bp3-button\\" tabindex=\\"0\\"><span icon=\\"tint\\" class=\\"bp3-icon bp3-icon-tint\\"><svg data-icon=\\"tint\\" width=\\"16\\" height=\\"16\\" viewBox=\\"0 0 16 16\\"><desc>tint</desc><path d=\\"M7.88 1s-4.9 6.28-4.9 8.9c.01 2.82 2.34 5.1 4.99 5.1 2.65-.01 5.03-2.3 5.03-5.13C12.99 7.17 7.88 1 7.88 1z\\" fill-rule=\\"evenodd\\"></path></svg></span></a></span></span></div></div><div style=\\"margin-left: 26px;\\"></div>"`;
@@ -1,5 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`did launch page launched 1`] = `"<span style=\\"max-width: 155px; display: flex; overflow: hidden; justify-content: flex-start; width: 100%; padding: 0px;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">pbm</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">c3k</span><span style=\\"position: absolute; right: 0px; color: inherit;\\">c3k</span></span></span>"`;
exports[`metadata loads categories and values from dataset appear 1`] = `"<div style=\\"display: flex; justify-content: space-between; align-items: baseline;\\"><div style=\\"display: flex; justify-content: flex-start; align-items: flex-start;\\"><label class=\\"bp3-control bp3-checkbox\\" for=\\"category-select-louvain\\"><input id=\\"category-select-louvain\\" data-testclass=\\"category-select\\" data-testid=\\"louvain:category-select\\" type=\\"checkbox\\" checked=\\"\\"><span class=\\"bp3-control-indicator\\"></span></label><span role=\\"menuitem\\" tabindex=\\"0\\" data-testclass=\\"category-expand\\" data-testid=\\"louvain:category-expand\\" style=\\"cursor: pointer;\\"><span class=\\"bp3-popover2-target\\"><span data-testid=\\"louvain:category-label\\" tabindex=\\"-1\\" aria-label=\\"louvain\\" class=\\"\\" style=\\"max-width: 265px;\\"><span style=\\"max-width: 265px; display: flex; overflow: hidden; justify-content: flex-start; width: 100%; padding: 0px;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">lou</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">vain</span><span style=\\"position: absolute; right: 0px; color: inherit;\\">vain</span></span></span></span></span><svg stroke=\\"currentColor\\" fill=\\"currentColor\\" stroke-width=\\"0\\" viewBox=\\"0 0 320 512\\" data-testclass=\\"category-expand-is-not-expanded\\" height=\\"1em\\" width=\\"1em\\" xmlns=\\"http://www.w3.org/2000/svg\\" style=\\"font-size: 10px; margin-left: 5px;\\"><path d=\\"M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z\\"></path></svg></span></div><div><span class=\\"bp3-popover-wrapper\\"><span aria-haspopup=\\"true\\" class=\\"bp3-popover-target\\"><a role=\\"button\\" data-testclass=\\"colorby\\" data-testid=\\"colorby-louvain\\" class=\\"bp3-button\\" tabindex=\\"0\\"><span icon=\\"tint\\" class=\\"bp3-icon bp3-icon-tint\\"><svg data-icon=\\"tint\\" width=\\"16\\" height=\\"16\\" viewBox=\\"0 0 16 16\\"><desc>tint</desc><path d=\\"M7.88 1s-4.9 6.28-4.9 8.9c.01 2.82 2.34 5.1 4.99 5.1 2.65-.01 5.03-2.3 5.03-5.13C12.99 7.17 7.88 1 7.88 1z\\" fill-rule=\\"evenodd\\"></path></svg></span></a></span></span></div></div><div style=\\"margin-left: 26px;\\"></div>"`;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+506
View File
@@ -0,0 +1,506 @@
/* eslint-disable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
import { strict as assert } from "assert";
import {
clearInputAndTypeInto,
clickOn,
getAllByClass,
getOneElementInnerText,
typeInto,
waitByID,
waitByClass,
waitForAllByIds,
clickOnUntil,
getTestClass,
getTestId,
isElementPresent,
goToPage,
} from "./puppeteerUtils";
import { appUrlBase, TEST_EMAIL, TEST_PASSWORD } from "./config";
export async function drag(testId, start, end, lasso = false) {
const layout = await waitByID(testId);
const elBox = await layout.boxModel();
const x1 = elBox.content[0].x + start.x;
const x2 = elBox.content[0].x + end.x;
const y1 = elBox.content[0].y + start.y;
const y2 = elBox.content[0].y + end.y;
await page.mouse.move(x1, y1);
await page.mouse.down();
if (lasso) {
await page.mouse.move(x2, y1);
await page.mouse.move(x2, y2);
await page.mouse.move(x1, y2);
await page.mouse.move(x1, y1);
} else {
await page.mouse.move(x2, y2);
}
await page.mouse.up();
}
export async function clickOnCoordinate(testId, coord) {
const layout = await expect(page).toMatchElement(getTestId(testId));
const elBox = await layout.boxModel();
if (!elBox) {
throw Error("Layout's boxModel is not available!");
}
const x = elBox.content[0].x + coord.x;
const y = elBox.content[0].y + coord.y;
await page.mouse.click(x, y);
}
export async function getAllHistograms(testclass, testIds) {
const histTestIds = testIds.map((tid) => `histogram-${tid}`);
// these load asynchronously, so we need to wait for each histogram individually,
// and they may be quite slow in some cases.
await waitForAllByIds(histTestIds, { timeout: 4 * 60 * 1000 });
const allHistograms = await getAllByClass(testclass);
const testIDs = await Promise.all(
allHistograms.map((hist) => page.evaluate((elem) => elem.dataset.testid, hist))
);
return testIDs.map((id) => id.replace(/^histogram-/, ""));
}
export async function getAllCategoriesAndCounts(category) {
// these load asynchronously, so we have to wait for the specific category.
await waitByID(`category-${category}`);
return page.$$eval(
`[data-testid="category-${category}"] [data-testclass='categorical-row']`,
(rows) =>
Object.fromEntries(
rows.map((row) => {
const cat = row
.querySelector("[data-testclass='categorical-value']")
.getAttribute("aria-label");
const count = row.querySelector(
"[data-testclass='categorical-value-count']"
).innerText;
return [cat, count];
})
)
);
}
export async function getCellSetCount(num) {
await clickOn(`cellset-button-${num}`);
return getOneElementInnerText(`[data-testid='cellset-count-${num}']`);
}
export async function resetCategory(category) {
const checkboxId = `${category}:category-select`;
await waitByID(checkboxId);
const checkedPseudoclass = await page.$eval(
`[data-testid='${checkboxId}']`,
(el) => el.matches(":checked")
);
if (!checkedPseudoclass) await clickOn(checkboxId);
const categoryRow = await waitByID(`${category}:category-expand`);
const isExpanded = await categoryRow.$(
"[data-testclass='category-expand-is-expanded']"
);
if (isExpanded) await clickOn(`${category}:category-expand`);
}
export async function calcCoordinate(testId, xAsPercent, yAsPercent) {
const el = await waitByID(testId);
const size = await el.boxModel();
return {
x: Math.floor(size.width * xAsPercent),
y: Math.floor(size.height * yAsPercent),
};
}
export async function calcDragCoordinates(testId, coordinateAsPercent) {
return {
start: await calcCoordinate(
testId,
coordinateAsPercent.x1,
coordinateAsPercent.y1
),
end: await calcCoordinate(
testId,
coordinateAsPercent.x2,
coordinateAsPercent.y2
),
};
}
export async function selectCategory(category, values, reset = true) {
if (reset) await resetCategory(category);
await clickOn(`${category}:category-expand`);
await clickOn(`${category}:category-select`);
for (const value of values) {
await clickOn(`categorical-value-select-${category}-${value}`);
}
}
export async function expandCategory(category) {
const expand = await waitByID(`${category}:category-expand`);
const notExpanded = await expand.$(
"[data-testclass='category-expand-is-not-expanded']"
);
if (notExpanded) await clickOn(`${category}:category-expand`);
}
export async function clip(min = 0, max = 100) {
await clickOn("visualization-settings");
await clearInputAndTypeInto("clip-min-input", min);
await clearInputAndTypeInto("clip-max-input", max);
await clickOn("clip-commit");
}
export async function createCategory(categoryName) {
await clickOnUntil("open-annotation-dialog", async () => {
await expect(page).toMatchElement(getTestId("new-category-name"));
});
await typeInto("new-category-name", categoryName);
await clickOn("submit-category");
}
/*
GENESET
*/
export async function colorByGeneset(genesetName) {
await clickOn(`${genesetName}:colorby-entire-geneset`);
}
export async function colorByGene(gene) {
await clickOn(`colorby-${gene}`);
}
export async function assertColorLegendLabel(label) {
const handle = await waitByID("continuous_legend_color_by_label");
const result = await handle.evaluate((node) => node.getAttribute("aria-label"));
return expect(result).toBe(label);
}
export async function expandGeneset(genesetName) {
const expand = await waitByID(`${genesetName}:geneset-expand`);
const notExpanded = await expand.$(
"[data-testclass='geneset-expand-is-not-expanded']"
);
if (notExpanded) await clickOn(`${genesetName}:geneset-expand`);
}
export async function createGeneset(genesetName) {
await clickOnUntil("open-create-geneset-dialog", async () => {
await expect(page).toMatchElement(getTestId("create-geneset-input"));
});
await typeInto("create-geneset-input", genesetName);
await clickOn("submit-geneset");
await waitByClass("autosave-complete");
}
export async function editGenesetName(genesetName, editText) {
const editButton = `${genesetName}:edit-genesetName-mode`;
const submitButton = `${genesetName}:submit-geneset`;
await clickOnUntil(`${genesetName}:see-actions`, async () => {
await expect(page).toMatchElement(getTestId(editButton));
});
await clickOn(editButton);
await typeInto("rename-geneset-modal", editText);
await clickOn(submitButton);
}
export async function deleteGeneset(genesetName) {
const targetId = `${genesetName}:delete-geneset`;
await clickOnUntil(`${genesetName}:see-actions`, async () => {
await expect(page).toMatchElement(getTestId(targetId));
});
await clickOn(targetId);
await assertGenesetDoesNotExist(genesetName);
await waitByClass("autosave-complete");
}
export async function assertGenesetDoesNotExist(genesetName) {
const result = await isElementPresent(
getTestId(`${genesetName}:geneset-name`)
);
await expect(result).toBe(false);
}
export async function assertGenesetExists(genesetName) {
const handle = await waitByID(`${genesetName}:geneset-name`);
const result = await handle.evaluate((node) => node.getAttribute("aria-label"));
return expect(result).toBe(genesetName);
}
/*
GENE
*/
export async function addGeneToSet(genesetName, geneToAddToSet) {
const submitButton = `${genesetName}:submit-gene`;
await clickOn(`${genesetName}:add-new-gene-to-geneset`);
await typeInto("add-genes", geneToAddToSet);
await clickOn(submitButton);
}
export async function removeGene(geneSymbol) {
const targetId = `delete-from-geneset:${geneSymbol}`;
await clickOn(targetId);
await waitByClass("autosave-complete");
}
export async function assertGeneExistsInGeneset(geneSymbol) {
const handle = await waitByID(`${geneSymbol}:gene-label`);
const result = await handle.evaluate((node) => node.getAttribute("aria-label"));
return expect(result).toBe(geneSymbol);
}
export async function assertGeneDoesNotExist(geneSymbol) {
const result = await isElementPresent(getTestId(`${geneSymbol}:gene-label`));
await expect(result).toBe(false);
}
export async function expandGene(geneSymbol) {
await clickOn(`maximize-${geneSymbol}`);
}
/*
CATEGORY
*/
export async function duplicateCategory(categoryName) {
await clickOn("open-annotation-dialog");
await typeInto("new-category-name", categoryName);
const dropdownOptionClass = "duplicate-category-dropdown-option";
await clickOnUntil("duplicate-category-dropdown", async () => {
await expect(page).toMatchElement(getTestClass(dropdownOptionClass));
});
const option = await expect(page).toMatchElement(
getTestClass(dropdownOptionClass)
);
await option.click();
await clickOnUntil("submit-category", async () => {
await expect(page).toMatchElement(
getTestId(`${categoryName}:category-expand`)
);
});
await waitByClass("autosave-complete");
}
export async function renameCategory(oldCategoryName, newCategoryName) {
await clickOn(`${oldCategoryName}:see-actions`);
await clickOn(`${oldCategoryName}:edit-category-mode`);
await clearInputAndTypeInto(
`${oldCategoryName}:edit-category-name-text`,
newCategoryName
);
await clickOn(`${oldCategoryName}:submit-category-edit`);
}
export async function deleteCategory(categoryName) {
const targetId = `${categoryName}:delete-category`;
await clickOnUntil(`${categoryName}:see-actions`, async () => {
await expect(page).toMatchElement(getTestId(targetId));
});
await clickOn(targetId);
await assertCategoryDoesNotExist();
}
export async function createLabel(categoryName, labelName) {
/**
* (thuang): This explicit wait is needed, since currently showing
* the modal again quickly after the previous action dismissing the
* modal will persist the input value from the previous action.
*
* To reproduce:
* 1. Click on the plus sign to show the modal to add a new label to the category
* 2. Type `123` in the input box
* 3. Hover over your mouse over the plus sign and double click to quickly dismiss and
* invoke the modal again
* 4. You will see `123` is persisted in the input box
* 5. Expected behavior is to get an empty input box
*/
await page.waitForTimeout(500);
await clickOn(`${categoryName}:see-actions`);
await clickOn(`${categoryName}:add-new-label-to-category`);
await typeInto(`${categoryName}:new-label-name`, labelName);
await clickOn(`${categoryName}:submit-label`);
}
export async function deleteLabel(categoryName, labelName) {
await expandCategory(categoryName);
await clickOn(`${categoryName}:${labelName}:see-actions`);
await clickOn(`${categoryName}:${labelName}:delete-label`);
}
export async function renameLabel(categoryName, oldLabelName, newLabelName) {
await expandCategory(categoryName);
await clickOn(`${categoryName}:${oldLabelName}:see-actions`);
await clickOn(`${categoryName}:${oldLabelName}:edit-label`);
await clearInputAndTypeInto(
`${categoryName}:${oldLabelName}:edit-label-name`,
newLabelName
);
await clickOn(`${categoryName}:${oldLabelName}:submit-label-edit`);
}
export async function addGeneToSearch(geneName) {
await typeInto("gene-search", geneName);
await page.keyboard.press("Enter");
await page.waitForSelector(`[data-testid='histogram-${geneName}']`);
}
export async function subset(coordinatesAsPercent) {
// In order to deselect the selection after the subset, make sure we have some clear part
// of the scatterplot we can click on
assert(coordinatesAsPercent.x2 < 0.99 || coordinatesAsPercent.y2 < 0.99);
const lassoSelection = await calcDragCoordinates(
"layout-graph",
coordinatesAsPercent
);
await drag("layout-graph", lassoSelection.start, lassoSelection.end, true);
await clickOn("subset-button");
const clearCoordinate = await calcCoordinate("layout-graph", 0.5, 0.99);
await clickOnCoordinate("layout-graph", clearCoordinate);
}
export async function setSellSet(cellSet, cellSetNum) {
const selections = cellSet.filter((sel) => sel.kind === "categorical");
for (const selection of selections) {
await selectCategory(selection.metadata, selection.values, true);
}
await getCellSetCount(cellSetNum);
}
export async function runDiffExp(cellSet1, cellSet2) {
await setSellSet(cellSet1, 1);
await setSellSet(cellSet2, 2);
await clickOn("diffexp-button");
}
export async function bulkAddGenes(geneNames) {
await clickOn("section-bulk-add");
await typeInto("input-bulk-add", geneNames.join(","));
await page.keyboard.press("Enter");
}
export async function assertCategoryDoesNotExist(categoryName) {
const result = await isElementPresent(
getTestId(`${categoryName}:category-label`)
);
await expect(result).toBe(false);
}
export async function login() {
await goToPage(appUrlBase);
await clickOn("log-in");
// (thuang): Auth0 form is unstable and unsafe for input until verified
await waitUntilFormFieldStable('[name="email"]');
await expect(page).toFillForm("form", {
email: TEST_EMAIL,
password: TEST_PASSWORD,
});
await Promise.all([
page.waitForNavigation({ waitUntil: "networkidle0" }),
expect(page).toClick('[name="submit"]'),
]);
expect(page.url()).toContain(appUrlBase);
}
export async function logout() {
await clickOnUntil("user-info", async () => {
await waitByID("log-out");
await Promise.all([
page.waitForNavigation({ waitUntil: "networkidle0" }),
clickOn("log-out"),
]);
});
await waitByID("log-in");
}
async function waitUntilFormFieldStable(selector) {
const MAX_RETRY = 10;
const WAIT_FOR_MS = 200;
const EXPECTED_VALUE = "aaa";
let retry = 0;
while (retry < MAX_RETRY) {
try {
await expect(page).toFill(selector, EXPECTED_VALUE);
const fieldHandle = await expect(page).toMatchElement(selector);
const fieldValue = await page.evaluate(
(input) => input.value,
fieldHandle
);
expect(fieldValue).toBe(EXPECTED_VALUE);
break;
} catch (error) {
retry += 1;
await page.waitForTimeout(WAIT_FOR_MS);
}
}
if (retry === MAX_RETRY) {
throw Error("clickOnUntil() assertion failed!");
}
}
/* eslint-enable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
-596
View File
@@ -1,596 +0,0 @@
/* eslint-disable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
import { strict as assert } from "assert";
import {
clearInputAndTypeInto,
clickOn,
getAllByClass,
getOneElementInnerText,
typeInto,
waitByID,
waitByClass,
waitForAllByIds,
clickOnUntil,
getTestClass,
getTestId,
isElementPresent,
goToPage,
} from "./puppeteerUtils";
import { appUrlBase, TEST_EMAIL, TEST_PASSWORD } from "./config";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function drag(testId: any, start: any, end: any, lasso = false) {
const layout = await waitByID(testId);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const elBox = await layout.boxModel();
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const x1 = elBox.content[0].x + start.x;
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const x2 = elBox.content[0].x + end.x;
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const y1 = elBox.content[0].y + start.y;
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const y2 = elBox.content[0].y + end.y;
await page.mouse.move(x1, y1);
await page.mouse.down();
if (lasso) {
await page.mouse.move(x2, y1);
await page.mouse.move(x2, y2);
await page.mouse.move(x1, y2);
await page.mouse.move(x1, y1);
} else {
await page.mouse.move(x2, y2);
}
await page.mouse.up();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function clickOnCoordinate(testId: any, coord: any) {
const layout = await expect(page).toMatchElement(getTestId(testId));
const elBox = await layout.boxModel();
if (!elBox) {
throw Error("Layout's boxModel is not available!");
}
const x = elBox.content[0].x + coord.x;
const y = elBox.content[0].y + coord.y;
await page.mouse.click(x, y);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getAllHistograms(testclass: any, testIds: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const histTestIds = testIds.map((tid: any) => `histogram-${tid}`);
// these load asynchronously, so we need to wait for each histogram individually,
// and they may be quite slow in some cases.
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 2.
await waitForAllByIds(histTestIds, { timeout: 4 * 60 * 1000 });
const allHistograms = await getAllByClass(testclass);
const testIDs = await Promise.all(
allHistograms.map((hist) =>
page.evaluate((elem) => elem.dataset.testid, hist)
)
);
return testIDs.map((id) => id.replace(/^histogram-/, ""));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getAllCategoriesAndCounts(category: any) {
// these load asynchronously, so we have to wait for the specific category.
await waitByID(`category-${category}`);
return page.$$eval(
`[data-testid="category-${category}"] [data-testclass='categorical-row']`,
(rows) =>
Object.fromEntries(
rows.map((row) => {
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const cat = row
.querySelector("[data-testclass='categorical-value']")
.getAttribute("aria-label");
const count = (row.querySelector(
"[data-testclass='categorical-value-count']"
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
) as any).innerText;
return [cat, count];
})
)
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getCellSetCount(num: any) {
await clickOn(`cellset-button-${num}`);
return getOneElementInnerText(`[data-testid='cellset-count-${num}']`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function resetCategory(category: any) {
const checkboxId = `${category}:category-select`;
await waitByID(checkboxId);
const checkedPseudoclass = await page.$eval(
`[data-testid='${checkboxId}']`,
(el) => el.matches(":checked")
);
if (!checkedPseudoclass) await clickOn(checkboxId);
const categoryRow = await waitByID(`${category}:category-expand`);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const isExpanded = await categoryRow.$(
"[data-testclass='category-expand-is-expanded']"
);
if (isExpanded) await clickOn(`${category}:category-expand`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function calcCoordinate(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
testId: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
xAsPercent: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
yAsPercent: any
) {
const el = await waitByID(testId);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const size = await el.boxModel();
return {
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
x: Math.floor(size.width * xAsPercent),
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
y: Math.floor(size.height * yAsPercent),
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function calcDragCoordinates(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
testId: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
coordinateAsPercent: any
) {
return {
start: await calcCoordinate(
testId,
coordinateAsPercent.x1,
coordinateAsPercent.y1
),
end: await calcCoordinate(
testId,
coordinateAsPercent.x2,
coordinateAsPercent.y2
),
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function selectCategory(category: any, values: any, reset = true) {
if (reset) await resetCategory(category);
await clickOn(`${category}:category-expand`);
await clickOn(`${category}:category-select`);
for (const value of values) {
await clickOn(`categorical-value-select-${category}-${value}`);
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function expandCategory(category: any) {
const expand = await waitByID(`${category}:category-expand`);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const notExpanded = await expand.$(
"[data-testclass='category-expand-is-not-expanded']"
);
if (notExpanded) await clickOn(`${category}:category-expand`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function clip(min = 0, max = 100) {
await clickOn("visualization-settings");
await clearInputAndTypeInto("clip-min-input", min);
await clearInputAndTypeInto("clip-max-input", max);
await clickOn("clip-commit");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function createCategory(categoryName: any) {
await clickOnUntil("open-annotation-dialog", async () => {
await expect(page).toMatchElement(getTestId("new-category-name"));
});
await typeInto("new-category-name", categoryName);
await clickOn("submit-category");
}
/**
* GENESET
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function colorByGeneset(genesetName: any) {
await clickOn(`${genesetName}:colorby-entire-geneset`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function colorByGene(gene: any) {
await clickOn(`colorby-${gene}`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function assertColorLegendLabel(label: any) {
const handle = await waitByID("continuous_legend_color_by_label");
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const result = await handle.evaluate((node) =>
node.getAttribute("aria-label")
);
return expect(result).toBe(label);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function expandGeneset(genesetName: any) {
const expand = await waitByID(`${genesetName}:geneset-expand`);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const notExpanded = await expand.$(
"[data-testclass='geneset-expand-is-not-expanded']"
);
if (notExpanded) await clickOn(`${genesetName}:geneset-expand`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function createGeneset(genesetName: any) {
await clickOnUntil("open-create-geneset-dialog", async () => {
await expect(page).toMatchElement(getTestId("create-geneset-input"));
});
await typeInto("create-geneset-input", genesetName);
await clickOn("submit-geneset");
await waitByClass("autosave-complete");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function editGenesetName(genesetName: any, editText: any) {
const editButton = `${genesetName}:edit-genesetName-mode`;
const submitButton = `${genesetName}:submit-geneset`;
await clickOnUntil(`${genesetName}:see-actions`, async () => {
await expect(page).toMatchElement(getTestId(editButton));
});
await clickOn(editButton);
await typeInto("rename-geneset-modal", editText);
await clickOn(submitButton);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function deleteGeneset(genesetName: any) {
const targetId = `${genesetName}:delete-geneset`;
await clickOnUntil(`${genesetName}:see-actions`, async () => {
await expect(page).toMatchElement(getTestId(targetId));
});
await clickOn(targetId);
await assertGenesetDoesNotExist(genesetName);
await waitByClass("autosave-complete");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function assertGenesetDoesNotExist(genesetName: any) {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const result = await isElementPresent(
getTestId(`${genesetName}:geneset-name`)
);
await expect(result).toBe(false);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function assertGenesetExists(genesetName: any) {
const handle = await waitByID(`${genesetName}:geneset-name`);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const result = await handle.evaluate((node) =>
node.getAttribute("aria-label")
);
return expect(result).toBe(genesetName);
}
/**
* GENE
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function addGeneToSet(genesetName: any, geneToAddToSet: any) {
const submitButton = `${genesetName}:submit-gene`;
await clickOn(`${genesetName}:add-new-gene-to-geneset`);
await typeInto("add-genes", geneToAddToSet);
await clickOn(submitButton);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function removeGene(geneSymbol: any) {
const targetId = `delete-from-geneset:${geneSymbol}`;
await clickOn(targetId);
await waitByClass("autosave-complete");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function assertGeneExistsInGeneset(geneSymbol: any) {
const handle = await waitByID(`${geneSymbol}:gene-label`);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const result = await handle.evaluate((node) =>
node.getAttribute("aria-label")
);
return expect(result).toBe(geneSymbol);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function assertGeneDoesNotExist(geneSymbol: any) {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const result = await isElementPresent(getTestId(`${geneSymbol}:gene-label`));
await expect(result).toBe(false);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function expandGene(geneSymbol: any) {
await clickOn(`maximize-${geneSymbol}`);
}
/**
* CATEGORY
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function duplicateCategory(categoryName: any) {
await clickOn("open-annotation-dialog");
await typeInto("new-category-name", categoryName);
const dropdownOptionClass = "duplicate-category-dropdown-option";
await clickOnUntil("duplicate-category-dropdown", async () => {
await expect(page).toMatchElement(getTestClass(dropdownOptionClass));
});
const option = await expect(page).toMatchElement(
getTestClass(dropdownOptionClass)
);
await option.click();
await clickOnUntil("submit-category", async () => {
await expect(page).toMatchElement(
getTestId(`${categoryName}:category-expand`)
);
});
await waitByClass("autosave-complete");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function renameCategory(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
oldCategoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newCategoryName: any
) {
await clickOn(`${oldCategoryName}:see-actions`);
await clickOn(`${oldCategoryName}:edit-category-mode`);
await clearInputAndTypeInto(
`${oldCategoryName}:edit-category-name-text`,
newCategoryName
);
await clickOn(`${oldCategoryName}:submit-category-edit`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function deleteCategory(categoryName: any) {
const targetId = `${categoryName}:delete-category`;
await clickOnUntil(`${categoryName}:see-actions`, async () => {
await expect(page).toMatchElement(getTestId(targetId));
});
await clickOn(targetId);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
await assertCategoryDoesNotExist();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function createLabel(categoryName: any, labelName: any) {
/**
* (thuang): This explicit wait is needed, since currently showing
* the modal again quickly after the previous action dismissing the
* modal will persist the input value from the previous action.
*
* To reproduce:
* 1. Click on the plus sign to show the modal to add a new label to the category
* 2. Type `123` in the input box
* 3. Hover over your mouse over the plus sign and double click to quickly dismiss and
* invoke the modal again
* 4. You will see `123` is persisted in the input box
* 5. Expected behavior is to get an empty input box
*/
await page.waitForTimeout(500);
await clickOn(`${categoryName}:see-actions`);
await clickOn(`${categoryName}:add-new-label-to-category`);
await typeInto(`${categoryName}:new-label-name`, labelName);
await clickOn(`${categoryName}:submit-label`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function deleteLabel(categoryName: any, labelName: any) {
await expandCategory(categoryName);
await clickOn(`${categoryName}:${labelName}:see-actions`);
await clickOn(`${categoryName}:${labelName}:delete-label`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function renameLabel(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
oldLabelName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newLabelName: any
) {
await expandCategory(categoryName);
await clickOn(`${categoryName}:${oldLabelName}:see-actions`);
await clickOn(`${categoryName}:${oldLabelName}:edit-label`);
await clearInputAndTypeInto(
`${categoryName}:${oldLabelName}:edit-label-name`,
newLabelName
);
await clickOn(`${categoryName}:${oldLabelName}:submit-label-edit`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function addGeneToSearch(geneName: any) {
await typeInto("gene-search", geneName);
await page.keyboard.press("Enter");
await page.waitForSelector(`[data-testid='histogram-${geneName}']`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function subset(coordinatesAsPercent: any) {
// In order to deselect the selection after the subset, make sure we have some clear part
// of the scatterplot we can click on
assert(coordinatesAsPercent.x2 < 0.99 || coordinatesAsPercent.y2 < 0.99);
const lassoSelection = await calcDragCoordinates(
"layout-graph",
coordinatesAsPercent
);
await drag("layout-graph", lassoSelection.start, lassoSelection.end, true);
await clickOn("subset-button");
const clearCoordinate = await calcCoordinate("layout-graph", 0.5, 0.99);
await clickOnCoordinate("layout-graph", clearCoordinate);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function setSellSet(cellSet: any, cellSetNum: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const selections = cellSet.filter((sel: any) => sel.kind === "categorical");
for (const selection of selections) {
await selectCategory(selection.metadata, selection.values, true);
}
await getCellSetCount(cellSetNum);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function runDiffExp(cellSet1: any, cellSet2: any) {
await setSellSet(cellSet1, 1);
await setSellSet(cellSet2, 2);
await clickOn("diffexp-button");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function bulkAddGenes(geneNames: any) {
await clickOn("section-bulk-add");
await typeInto("input-bulk-add", geneNames.join(","));
await page.keyboard.press("Enter");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function assertCategoryDoesNotExist(categoryName: any) {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const result = await isElementPresent(
getTestId(`${categoryName}:category-label`)
);
await expect(result).toBe(false);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function login() {
await goToPage(appUrlBase);
await clickOn("log-in");
// (thuang): Auth0 form is unstable and unsafe for input until verified
await waitUntilFormFieldStable('[name="email"]');
await expect(page).toFillForm("form", {
email: TEST_EMAIL,
password: TEST_PASSWORD,
});
await Promise.all([
page.waitForNavigation({ waitUntil: "networkidle0" }),
expect(page).toClick('[name="submit"]'),
]);
expect(page.url()).toContain(appUrlBase);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function logout() {
await clickOnUntil("user-info", async () => {
await waitByID("log-out");
await Promise.all([
page.waitForNavigation({ waitUntil: "networkidle0" }),
clickOn("log-out"),
]);
});
await waitByID("log-in");
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function waitUntilFormFieldStable(selector: any) {
const MAX_RETRY = 10;
const WAIT_FOR_MS = 200;
const EXPECTED_VALUE = "aaa";
let retry = 0;
while (retry < MAX_RETRY) {
try {
await expect(page).toFill(selector, EXPECTED_VALUE);
const fieldHandle = await expect(page).toMatchElement(selector);
const fieldValue = await page.evaluate(
(input) => input.value,
fieldHandle
);
expect(fieldValue).toBe(EXPECTED_VALUE);
break;
} catch (error) {
retry += 1;
await page.waitForTimeout(WAIT_FOR_MS);
}
}
if (retry === MAX_RETRY) {
throw Error("clickOnUntil() assertion failed!");
}
}
/* eslint-enable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
@@ -58,12 +58,10 @@ describe("metadata loads", () => {
const categories = await getAllCategoriesAndCounts(label);
expect(Object.keys(categories)).toMatchObject(
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
Object.keys(data.categorical[label])
);
expect(Object.values(categories)).toMatchObject(
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
Object.values(data.categorical[label])
);
}
@@ -161,12 +159,10 @@ describe("subset", () => {
const categories = await getAllCategoriesAndCounts(label);
expect(Object.keys(categories)).toMatchObject(
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
Object.keys(data.subset.categorical[label])
);
expect(Object.values(categories)).toMatchObject(
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
Object.values(data.subset.categorical[label])
);
}
@@ -199,7 +195,6 @@ describe("clipping", () => {
test("clip continuous", async () => {
await goToPage(appUrlBase);
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
await clip(data.clip.min, data.clip.max);
const histBrushableAreaId = `histogram-${data.clip.metadata}-plot-brushable-area`;
const coords = await calcDragCoordinates(
@@ -259,7 +254,6 @@ describe("centroid labels", () => {
const generatedLabels = await getAllByClass("centroid-label");
// Number of labels generated should be equal to size of the object
expect(generatedLabels).toHaveLength(
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
Object.keys(data.categorical[label]).length
);
}
@@ -281,7 +275,6 @@ describe("graph overlay", () => {
data.pan["coordinates-as-percent"]
);
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
const categoryValue = Object.keys(data.categorical[category])[0];
const initialCoordinates = await getElementCoordinates(
`${categoryValue}-centroid-label`
@@ -82,8 +82,7 @@ const genesetDescriptionID =
const genesetDescriptionString = "fourth_gene_set: fourth description";
const genesetToCheckForDescription = "fourth_gene_set";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function setup(config: any) {
async function setup(config) {
await goToPage(appUrlBase);
if (config.categoricalAnno) {
@@ -157,8 +156,7 @@ describe.each([
await expect(page).toClick(getTestClass("pop-1-geneset-expand"));
await page.waitForFunction(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(selector: any) => !document.querySelector(selector),
(selector) => !document.querySelector(selector),
{},
getTestClass("gene-loading-spinner")
);
@@ -173,8 +171,7 @@ describe.each([
await expect(page).toClick(getTestClass("pop-2-geneset-expand"));
await page.waitForFunction(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(selector: any) => !document.querySelector(selector),
(selector) => !document.querySelector(selector),
{},
getTestClass("gene-loading-spinner")
);
@@ -405,13 +402,8 @@ describe.each([
expect(actualLabelName).toBe(expectedLabelName);
expect(actualLabelCount).toBe(expectedLabelCount);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function getInnerText(element: any, className: any) {
return element.$eval(
getTestClass(className),
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(node: any) => node?.innerText
);
async function getInnerText(element, className) {
return element.$eval(getTestClass(className), (node) => node?.innerText);
}
});
@@ -436,9 +428,7 @@ describe.each([
`categorical-value-count-${perTestCategoryName}-${perTestLabelName}`
);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
expect(await result.evaluate((node) => node.innerText)).toBe(
// @ts-expect-error ts-migrate(2538) FIXME: Type 'boolean' cannot be used as an index type.
data.categoryLabel.newCount.bySubsetConfig[config.withSubset]
);
});
@@ -498,7 +488,6 @@ describe.each([
await createLabel(perTestCategoryName, labelName);
await assertLabelExists(perTestCategoryName, labelName);
await clickOn("undo");
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
await assertLabelDoesNotExist(perTestCategoryName);
await clickOn("redo");
await assertLabelExists(perTestCategoryName, labelName);
@@ -508,12 +497,10 @@ describe.each([
await setup(config);
await deleteLabel(perTestCategoryName, perTestLabelName);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
await assertLabelDoesNotExist(perTestCategoryName);
await clickOn("undo");
await assertLabelExists(perTestCategoryName, perTestLabelName);
await clickOn("redo");
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
await assertLabelDoesNotExist(perTestCategoryName);
});
@@ -544,9 +531,7 @@ describe.each([
const labels = await getAllByClass("categorical-row");
const result = await Promise.all(
labels.map((label) =>
page.evaluate((element) => element.outerHTML, label)
)
labels.map((label) => page.evaluate((element) => element.outerHTML, label))
);
expect(result).toMatchSnapshot();
@@ -574,11 +559,9 @@ describe.each([
expect(result).toMatchSnapshot();
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function assertCategoryExists(categoryName: any) {
async function assertCategoryExists(categoryName) {
const handle = await waitByID(`${categoryName}:category-label`);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const result = await handle.evaluate((node) =>
node.getAttribute("aria-label")
);
@@ -586,8 +569,7 @@ describe.each([
return expect(result).toBe(categoryName);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function assertLabelExists(categoryName: any, labelName: any) {
async function assertLabelExists(categoryName, labelName) {
await expect(page).toMatchElement(
getTestId(`${categoryName}:category-expand`)
);
@@ -599,13 +581,11 @@ describe.each([
);
expect(
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
await previous.evaluate((node) => node.getAttribute("aria-label"))
).toBe(labelName);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function assertLabelDoesNotExist(categoryName: any, labelName: any) {
async function assertLabelDoesNotExist(categoryName, labelName) {
await expandCategory(categoryName);
const result = await page.$(
`[data-testid='categorical-value-${categoryName}-${labelName}']`
+4 -4
View File
@@ -1,10 +1,10 @@
{
"testRunner": "jest-circus/runner",
"preset": "jest-puppeteer",
"testMatch": ["**/__tests__/**/?(*.)(spec|test).ts?(x)"],
"setupFiles": ["../setupMissingGlobals.ts"],
"setupFilesAfterEnv": ["expect-puppeteer", "./puppeteer.setup.ts"],
"globalSetup": "../globalSetup.ts",
"testMatch": ["**/__tests__/**/?(*.)(spec|test).js?(x)"],
"setupFiles": ["../setupMissingGlobals.js"],
"setupFilesAfterEnv": ["expect-puppeteer", "./puppeteer.setup.js"],
"globalSetup": "../globalSetup.js",
"globalTeardown": "jest-environment-puppeteer/teardown",
"testEnvironment": "./screenshot_env.js"
}
@@ -23,7 +23,6 @@ beforeEach(async () => {
const userAgent = await browser.userAgent();
await page.setUserAgent(`${userAgent}bot`);
// @ts-expect-error ts-migrate(2341) FIXME: Property '_client' is private and only accessible ... Remove this comment to see the full error message
await page._client.send("Animation.setPlaybackRate", { playbackRate: 12 });
page.on("pageerror", (err) => {
@@ -50,8 +49,7 @@ beforeEach(async () => {
}
const errorMsgText = await Promise.all(
// TODO can we do this without internal properties?
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
msg.args().map((arg: any) => arg._remoteObject.description)
msg.args().map((arg) => arg._remoteObject.description)
);
throw new Error(`Console error: ${errorMsgText}`);
}
+131
View File
@@ -0,0 +1,131 @@
/* eslint-disable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
export function getTestId(id) {
return `[data-testid='${id}']`;
}
export function getTestClass(className) {
return `[data-testclass='${className}']`;
}
export async function waitByID(testId, props = {}) {
return page.waitForSelector(getTestId(testId), props);
}
export async function waitByClass(testClass, props = {}) {
return page.waitForSelector(`[data-testclass='${testClass}']`, props);
}
export async function waitForAllByIds(testIds) {
await Promise.all(
testIds.map((testId) => page.waitForSelector(getTestId(testId)))
);
}
export async function getAllByClass(testClass) {
return page.$$(`[data-testclass=${testClass}]`);
}
export async function typeInto(testId, text) {
// blueprint's typeahead is treating typing weird, clicking & waiting first solves this
// only works for text without special characters
await waitByID(testId);
const selector = getTestId(testId);
// type ahead can be annoying if you don't pause before you type
await page.click(selector);
await page.waitForTimeout(200);
await page.type(selector, text);
}
export async function clearInputAndTypeInto(testId, text) {
await waitByID(testId);
const selector = getTestId(testId);
// only works for text without special characters
// type ahead can be annoying if you don't pause before you type
await page.click(selector);
await page.waitForTimeout(200);
// select all
await page.click(selector, { clickCount: 3 });
await page.keyboard.press("Backspace");
await page.type(selector, text);
}
export async function clickOn(testId, options = {}) {
await expect(page).toClick(getTestId(testId), options);
}
/**
* (thuang): There are times when Puppeteer clicks on a button and the page doesn't respond.
* So I added clickOnUntil() to retry clicking until a given condition is met.
*/
export async function clickOnUntil(testId, assert) {
const MAX_RETRY = 10;
const WAIT_FOR_MS = 200;
let retry = 0;
while (retry < MAX_RETRY) {
try {
await clickOn(testId);
await assert();
break;
} catch (error) {
retry += 1;
await page.waitForTimeout(WAIT_FOR_MS);
}
}
if (retry === MAX_RETRY) {
throw Error("clickOnUntil() assertion failed!");
}
}
export async function getOneElementInnerHTML(selector, options = {}) {
await page.waitForSelector(selector, options);
return page.$eval(selector, (el) => el.innerHTML);
}
export async function getOneElementInnerText(selector) {
expect(page).toMatchElement(selector);
return page.$eval(selector, (el) => el.innerText);
}
export async function getElementCoordinates(testId) {
return page.$eval(getTestId(testId), (elem) => {
const { left, top } = elem.getBoundingClientRect();
return [left, top];
});
}
async function clickTermsOfService() {
if (!(await isElementPresent(getTestId("tos-cookies-accept")))) return;
await clickOn("tos-cookies-accept");
}
async function nameNewAnnotation() {
if (await isElementPresent(getTestId("annotation-dialog"))) {
await typeInto("new-annotation-name", "ignoreE2E");
await clickOn("submit-annotation");
// wait for the page to load
await waitByClass("autosave-complete");
}
}
export async function goToPage(url) {
await page.goto(url, {
waitUntil: "networkidle0",
});
await nameNewAnnotation();
await clickTermsOfService();
}
export async function isElementPresent(selector, options) {
return Boolean(await page.$(selector, options));
}
/* eslint-enable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
-151
View File
@@ -1,151 +0,0 @@
/* eslint-disable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export function getTestId(id: any) {
return `[data-testid='${id}']`;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export function getTestClass(className: any) {
return `[data-testclass='${className}']`;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function waitByID(testId: any, props = {}) {
return page.waitForSelector(getTestId(testId), props);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function waitByClass(testClass: any, props = {}) {
return page.waitForSelector(`[data-testclass='${testClass}']`, props);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function waitForAllByIds(testIds: any) {
await Promise.all(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
testIds.map((testId: any) => page.waitForSelector(getTestId(testId)))
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getAllByClass(testClass: any) {
return page.$$(`[data-testclass=${testClass}]`);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function typeInto(testId: any, text: any) {
// blueprint's typeahead is treating typing weird, clicking & waiting first solves this
// only works for text without special characters
await waitByID(testId);
const selector = getTestId(testId);
// type ahead can be annoying if you don't pause before you type
await page.click(selector);
await page.waitForTimeout(200);
await page.type(selector, text);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function clearInputAndTypeInto(testId: any, text: any) {
await waitByID(testId);
const selector = getTestId(testId);
// only works for text without special characters
// type ahead can be annoying if you don't pause before you type
await page.click(selector);
await page.waitForTimeout(200);
// select all
await page.click(selector, { clickCount: 3 });
await page.keyboard.press("Backspace");
await page.type(selector, text);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function clickOn(testId: any, options = {}) {
await expect(page).toClick(getTestId(testId), options);
}
/**
* (thuang): There are times when Puppeteer clicks on a button and the page doesn't respond.
* So I added clickOnUntil() to retry clicking until a given condition is met.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function clickOnUntil(testId: any, assert: any) {
const MAX_RETRY = 10;
const WAIT_FOR_MS = 200;
let retry = 0;
while (retry < MAX_RETRY) {
try {
await clickOn(testId);
await assert();
break;
} catch (error) {
retry += 1;
await page.waitForTimeout(WAIT_FOR_MS);
}
}
if (retry === MAX_RETRY) {
throw Error("clickOnUntil() assertion failed!");
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getOneElementInnerHTML(selector: any, options = {}) {
await page.waitForSelector(selector, options);
return page.$eval(selector, (el) => el.innerHTML);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getOneElementInnerText(selector: any) {
expect(page).toMatchElement(selector);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return page.$eval(selector, (el) => (el as any).innerText);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function getElementCoordinates(testId: any) {
return page.$eval(getTestId(testId), (elem) => {
const { left, top } = elem.getBoundingClientRect();
return [left, top];
});
}
async function clickTermsOfService() {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
if (!(await isElementPresent(getTestId("tos-cookies-accept")))) return;
await clickOn("tos-cookies-accept");
}
async function nameNewAnnotation() {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
if (await isElementPresent(getTestId("annotation-dialog"))) {
await typeInto("new-annotation-name", "ignoreE2E");
await clickOn("submit-annotation");
// wait for the page to load
await waitByClass("autosave-complete");
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function goToPage(url: any) {
await page.goto(url, {
waitUntil: "networkidle0",
});
await nameNewAnnotation();
await clickTermsOfService();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export async function isElementPresent(selector: any, options: any) {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 2.
return Boolean(await page.$(selector, options));
}
/* eslint-enable no-await-in-loop -- await in loop is needed to emulate sequential user actions */
-4
View File
@@ -1,11 +1,7 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const PuppeteerEnvironment = require("jest-environment-puppeteer");
require("jest-circus");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const ENV_DEFAULT = require("../../../environment.default.json");
// @ts-expect-error ts-migrate(2451) FIXME: Cannot redeclare block-scoped variable 'takeScreen... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const takeScreenshot = require("./takeScreenshot");
class ScreenshotEnvironment extends PuppeteerEnvironment {
@@ -1,12 +1,8 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment --- FIXME: disabled temporarily on migrate to TS.
// @ts-ignore FIXME: 'globalSetup.ts' cannot be compiled under '--isola... Remove this comment to see the full error message
const {
SecretsManagerClient,
GetSecretValueCommand,
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
} = require("@aws-sdk/client-secrets-manager");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const { setup } = require("jest-environment-puppeteer");
const client = new SecretsManagerClient({ region: "us-west-2" });
@@ -20,16 +20,7 @@ describe("cascade", () => {
const reducer = cascadeReducers([
[
"foo",
(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
currentState: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
action: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
nextSharedState: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
prevSharedState: any
) => {
(currentState, action, nextSharedState, prevSharedState) => {
expect(currentState).toBeUndefined();
expect(action).toEqual(topLevelAction);
expect(nextSharedState).toStrictEqual({});
@@ -39,16 +30,7 @@ describe("cascade", () => {
],
[
"bar",
(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
currentState: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
action: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
nextSharedState: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
prevSharedState: any
) => {
(currentState, action, nextSharedState, prevSharedState) => {
expect(currentState).toBeUndefined();
expect(action).toEqual(topLevelAction);
expect(nextSharedState).toStrictEqual({ foo: 0 });
@@ -501,7 +501,6 @@ describe("geneset: set tid", () => {
test("not a number error", () => {
expect(() => {
genesetsReducer(
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number' is not assignable to type 'undefined... Remove this comment to see the full error message
{ lastTid: 1 },
{
type: "geneset: set tid",
@@ -514,7 +513,6 @@ describe("geneset: set tid", () => {
test("decrement error", () => {
expect(() => {
genesetsReducer(
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number' is not assignable to type 'undefined... Remove this comment to see the full error message
{ lastTid: 1 },
{
type: "geneset: set tid",
@@ -2,7 +2,6 @@ import undoable from "../../src/reducers/undoable";
describe("create", () => {
test("no keys", () => {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2-3 arguments, but got 1.
expect(() => undoable(() => {})).toThrow();
expect(() => undoable(() => {}, null)).toThrow();
expect(() => undoable(() => {}, [])).toThrow();
@@ -24,8 +23,7 @@ describe("create", () => {
describe("undo", () => {
test("expected state modifications", () => {
const initialState = { a: 0, b: 1000 };
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const reducer = (state: any) => ({ a: state.a + 1, b: state.b + 1 });
const reducer = (state) => ({ a: state.a + 1, b: state.b + 1 });
const undoableReducer = undoable(reducer, ["a"]);
const s1 = undoableReducer(initialState, { type: "test" });
@@ -43,10 +41,8 @@ describe("undo", () => {
describe("redo", () => {
const initialState = { a: 0, b: 1000 };
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const reducer = (state: any) => ({ a: state.a + 1, b: state.b + 1 });
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let UR: any;
const reducer = (state) => ({ a: state.a + 1, b: state.b + 1 });
let UR;
beforeEach(() => {
UR = undoable(reducer, ["a"]);
@@ -5,6 +5,5 @@ the jest test environment).
import { TextDecoder, TextEncoder } from "util";
// @ts-expect-error ts-migrate(2322) FIXME: Type 'typeof TextDecoder' is not assignable to typ... Remove this comment to see the full error message
global.TextDecoder = TextDecoder;
global.TextEncoder = TextEncoder;
@@ -11,18 +11,14 @@ describe("rangeEncodeIndices", () => {
test("sorted flag", () => {
expect(rangeEncodeIndices([1, 9, 432], 10, true)).toMatchObject([
1,
9,
432,
1, 9, 432,
]);
expect(rangeEncodeIndices([1, 9, 432], 10, false)).toMatchObject([
1,
9,
432,
1, 9, 432,
]);
expect(
rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true)
).toMatchObject([[0, 3], [9, 10], 432]);
expect(rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true)).toMatchObject(
[[0, 3], [9, 10], 432]
);
expect(
rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, false)
).toMatchObject([[0, 3], [9, 10], 432]);
@@ -14,13 +14,10 @@ import { Dataframe } from "../../../src/util/dataframe";
enableFetchMocks();
describe("AnnoMatrix", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let annoMatrix: any;
let annoMatrix;
beforeEach(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).resetMocks(); // reset all fetch mocking state
// reset all fetch mocking state
fetch.resetMocks(); // reset all fetch mocking state
annoMatrix = new AnnoMatrixLoader(
serverMocks.baseDataURL,
serverMocks.schema.schema
@@ -39,8 +36,7 @@ describe("AnnoMatrix", () => {
});
test("simple single column fetch", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(serverMocks.annotationsObs(["name_0"]));
fetch.once(serverMocks.annotationsObs(["name_0"]));
const df = await annoMatrix.fetch("obs", "name_0");
expect(df).toBeInstanceOf(Dataframe);
@@ -49,8 +45,7 @@ describe("AnnoMatrix", () => {
});
test("simple multi column fetch", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any)
fetch
.once(serverMocks.annotationsObs(["name_0"]))
.once(serverMocks.annotationsObs(["n_genes"]));
@@ -60,13 +55,9 @@ describe("AnnoMatrix", () => {
});
describe("fetch from field", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const getLastTwo = async (field: any) => {
const getLastTwo = async (field) => {
const columnNames = annoMatrix.getMatrixColumns(field).slice(-2);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockResponses(
...columnNames.map(() => serverMocks.responder)
);
fetch.mockResponses(...columnNames.map(() => serverMocks.responder));
await expect(
annoMatrix.fetch(field, columnNames)
).resolves.toBeInstanceOf(Dataframe);
@@ -79,22 +70,19 @@ describe("AnnoMatrix", () => {
test("fetch - test all query forms", async () => {
// single string is a column name
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(serverMocks.annotationsObs(["n_genes"]));
fetch.once(serverMocks.annotationsObs(["n_genes"]));
await expect(annoMatrix.fetch("obs", "n_genes")).resolves.toBeInstanceOf(
Dataframe
);
// array of column names, expecting n_genes to be cached.
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(serverMocks.annotationsObs(["percent_mito"]));
fetch.once(serverMocks.annotationsObs(["percent_mito"]));
await expect(
annoMatrix.fetch("obs", ["n_genes", "percent_mito"])
).resolves.toBeInstanceOf(Dataframe);
// more complex value filter query, enumerated
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(serverMocks.responder);
fetch.once(serverMocks.responder);
await expect(
annoMatrix.fetch("X", {
where: {
@@ -107,8 +95,7 @@ describe("AnnoMatrix", () => {
// more complex value filter query, range
const varIndex = annoMatrix.schema.annotations.var.index;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any)
fetch
.once(
serverMocks.withExpected("/data/var", [[`var:${varIndex}`, "SUMO3"]])
)
@@ -184,8 +171,7 @@ describe("AnnoMatrix", () => {
expect(am1.nObs).toEqual(am2.nObs);
expect(am1.nVar).toEqual(am2.nVar);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any)
fetch
.once(serverMocks.annotationsObs(["n_genes"]))
.once(serverMocks.annotationsObs(["n_genes"]));
const ng1 = await am1.fetch("obs", "n_genes");
@@ -199,11 +185,9 @@ describe("AnnoMatrix", () => {
});
describe("add/drop column", () => {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'base' implicitly has an 'any' type.
async function addDrop(base) {
expect(base.getMatrixColumns("obs")).not.toContain("foo");
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(base.fetch("obs", "foo")).rejects.toThrow(
"unknown column name"
);
@@ -228,8 +212,7 @@ describe("AnnoMatrix", () => {
const am2 = am1.dropObsColumn("foo");
expect(base.getMatrixColumns("obs")).not.toContain("foo");
expect(am2.getMatrixColumns("obs")).not.toContain("foo");
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(am2.fetch("obs", "foo")).rejects.toThrow(
"unknown column name"
);
@@ -252,16 +235,14 @@ describe("AnnoMatrix", () => {
const am4 = clip(am3, 0, 1);
await addDrop(am4);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockResponse(serverMocks.responder);
fetch.mockResponse(serverMocks.responder);
await am1.fetch("obs", am1.getMatrixColumns("obs"));
await am2.fetch("obs", am2.getMatrixColumns("obs"));
await am3.fetch("obs", am3.getMatrixColumns("obs"));
await am4.fetch("obs", am4.getMatrixColumns("obs"));
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).resetMocks();
fetch.resetMocks();
await addDrop(am1);
await addDrop(am2);
@@ -271,7 +252,6 @@ describe("AnnoMatrix", () => {
});
describe("setObsColumnValues", () => {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'base' implicitly has an 'any' type.
async function addSetDrop(base) {
/* add column */
let am = base.addObsColumn(
@@ -307,8 +287,7 @@ describe("AnnoMatrix", () => {
);
/* drop column */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
am = am1.dropObsColumn("test");
await expect(am.fetch("obs", "test")).rejects.toThrow(
"unknown column name"
@@ -329,8 +308,7 @@ describe("AnnoMatrix", () => {
const am3 = isubset(annoMatrix, [10, 1, 0, 30, 2]);
await addSetDrop(am3);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockResponse(serverMocks.responder);
fetch.mockResponse(serverMocks.responder);
await am1.fetch("obs", am1.getMatrixColumns("obs"));
await am2.fetch("obs", am2.getMatrixColumns("obs"));
@@ -17,15 +17,11 @@ import { rangeFill } from "../../../src/util/range";
enableFetchMocks();
describe("AnnoMatrixCrossfilter", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let annoMatrix: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let crossfilter: any;
let annoMatrix;
let crossfilter;
beforeEach(async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).resetMocks(); // reset all fetch mocking state
// reset all fetch mocking state
fetch.resetMocks(); // reset all fetch mocking state
annoMatrix = new AnnoMatrixLoader(
serverMocks.baseDataURL,
serverMocks.schema.schema
@@ -71,10 +67,7 @@ describe("AnnoMatrixCrossfilter", () => {
crossfilter.obsCrossfilter.hasDimension("obs/louvain")
).toBeFalsy();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
);
fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain]));
let newCrossfilter = await crossfilter.select("obs", "louvain", {
mode: "none",
});
@@ -83,8 +76,7 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
newCrossfilter.obsCrossfilter.hasDimension("obs/louvain")
).toBeTruthy();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
expect((fetch as any).mock.calls).toHaveLength(1);
expect(fetch.mock.calls).toHaveLength(1);
newCrossfilter = await crossfilter.select("obs", "louvain", {
mode: "all",
@@ -95,10 +87,7 @@ describe("AnnoMatrixCrossfilter", () => {
test("simple column select", async () => {
let xfltr;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
);
fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain]));
xfltr = await crossfilter.select("obs", "louvain", {
mode: "exact",
values: ["NK cells", "B cells"],
@@ -116,7 +105,6 @@ describe("AnnoMatrixCrossfilter", () => {
expect(xfltr.allSelectedLabels()).toEqual(
Int32Array.from(
obsLouvain.reduce((acc, val, idx) => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number' is not assignable to par... Remove this comment to see the full error message
if (val === "NK cells" || val === "B cells") acc.push(idx);
return acc;
}, [])
@@ -136,13 +124,10 @@ describe("AnnoMatrixCrossfilter", () => {
const values = df.col("louvain").asArray();
const selected = xfltr.allSelectedMask();
values.every(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(val: any, idx: any) =>
!["NK cells", "B cells"].includes(val) !== !selected[idx]
(val, idx) => !["NK cells", "B cells"].includes(val) !== !selected[idx]
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
fetch.once(
serverMocks.dataframeResponse(["n_genes"], [new Int32Array(obsNGenes)])
);
xfltr = await xfltr.select("obs", "n_genes", {
@@ -161,7 +146,6 @@ describe("AnnoMatrixCrossfilter", () => {
val < 500 &&
(louvain === "NK cells" || louvain === "B cells")
)
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number' is not assignable to par... Remove this comment to see the full error message
acc.push(idx);
return acc;
}, [])
@@ -176,8 +160,7 @@ describe("AnnoMatrixCrossfilter", () => {
const varIndex = annoMatrix.schema.annotations.var.index;
const { nObs } = annoMatrix.schema.dataframe;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
fetch.once(
serverMocks.dataframeResponse(
["TEST"],
[rangeFill(new Float32Array(nObs), 0, 0.1)]
@@ -213,19 +196,14 @@ describe("AnnoMatrixCrossfilter", () => {
});
const values = df.icol(0).asArray();
const selected = xfltr.allSelectedMask();
values.every(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(val: any, idx: any) => !(val >= 0 && val <= 50) !== !selected[idx]
values.every((val, idx) => !(val >= 0 && val <= 50) !== !selected[idx]);
expect(selected.reduce((acc, val) => (val ? acc + 1 : acc), 0)).toEqual(
xfltr.countSelected()
);
expect(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
selected.reduce((acc: any, val: any) => (val ? acc + 1 : acc), 0)
).toEqual(xfltr.countSelected());
});
test("spatial column select", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
fetch.once(
serverMocks.dataframeResponse(
["umap_0", "umap_1"],
[Float32Array.from(embUmap[0]), Float32Array.from(embUmap[1])]
@@ -244,7 +222,6 @@ describe("AnnoMatrixCrossfilter", () => {
test("select on subset", async () => {
const mask = new Uint8Array(annoMatrix.nObs).fill(0);
for (let i = 0; i < mask.length; i += 2) {
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'number'.
mask[i] = true;
}
const annoMatrixSubset = isubsetMask(annoMatrix, mask);
@@ -253,10 +230,7 @@ describe("AnnoMatrixCrossfilter", () => {
let xfltr = new AnnoMatrixObsCrossfilter(annoMatrixSubset);
expect(xfltr.countSelected()).toEqual(annoMatrixSubset.nObs);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
);
fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain]));
xfltr = await xfltr.select("obs", "louvain", {
mode: "exact",
values: ["NK cells", "B cells"],
@@ -269,9 +243,7 @@ describe("AnnoMatrixCrossfilter", () => {
const values = df.col("louvain").asArray();
const selected = xfltr.allSelectedMask();
values.every(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(val: any, idx: any) =>
!["NK cells", "B cells"].includes(val) !== !selected[idx]
(val, idx) => !["NK cells", "B cells"].includes(val) !== !selected[idx]
);
});
@@ -284,8 +256,7 @@ describe("AnnoMatrixCrossfilter", () => {
"unable to obsSelect upon the var dimension"
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(crossfilter.select("obs", "foo")).rejects.toThrow(
"unknown column name"
);
@@ -296,29 +267,24 @@ describe("AnnoMatrixCrossfilter", () => {
/*
test the matrix mutators via crossfilter proxy
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function helperAddTestCol(cf: any, colName: any, colSchema = null) {
async function helperAddTestCol(cf, colName, colSchema = null) {
expect(
cf.annoMatrix.getMatrixColumns("obs").includes(colName)
).toBeFalsy();
if (colSchema === null) {
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ name: any; type: string; categories: strin... Remove this comment to see the full error message
colSchema = {
name: colName,
type: "categorical",
categories: ["toasty"],
};
}
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
colSchema.name = colName;
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
const initValue = colSchema.categories[0];
const xfltr = cf.addObsColumn(colSchema, Array, initValue);
expect(
xfltr.annoMatrix.schema.annotations.obs.columns.filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(v: any) => v.name === colName
(v) => v.name === colName
)
).toHaveLength(1);
const df = await xfltr.annoMatrix.fetch("obs", colName);
@@ -348,8 +314,7 @@ describe("AnnoMatrixCrossfilter", () => {
});
expect(
xfltr.annoMatrix.schema.annotations.obs.columns.filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(v: any) => v.name === "foo"
(v) => v.name === "foo"
)
).toHaveLength(1);
@@ -358,8 +323,8 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
df
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.every((v: any) => v === "A")
.asArray()
.every((v) => v === "A")
).toBeTruthy();
// check that we catch dups
@@ -396,13 +361,11 @@ describe("AnnoMatrixCrossfilter", () => {
xfltr = xfltr.dropObsColumn("foo");
expect(
xfltr.annoMatrix.schema.annotations.obs.columns.filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(v: any) => v.name === "foo"
(v) => v.name === "foo"
)
).toHaveLength(0);
expect(xfltr.annoMatrix.schema.annotations.obsByName.foo).toBeUndefined();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(xfltr.annoMatrix.fetch("obs", "foo")).rejects.toThrow(
"unknown column name"
);
@@ -415,8 +378,7 @@ describe("AnnoMatrixCrossfilter", () => {
});
xfltr = xfltr.dropObsColumn("bar");
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(xfltr.select("obs", "bar", { mode: "all" })).rejects.toThrow(
"unknown column name"
);
@@ -442,8 +404,7 @@ describe("AnnoMatrixCrossfilter", () => {
type: "categorical",
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(xfltr.annoMatrix.fetch("obs", "foo")).rejects.toThrow(
"unknown column name"
);
@@ -458,8 +419,7 @@ describe("AnnoMatrixCrossfilter", () => {
});
xfltr = xfltr.renameObsColumn("bar", "xyz");
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).mockRejectOnce(new Error("unknown column name"));
fetch.mockRejectOnce(new Error("unknown column name"));
await expect(xfltr.select("obs", "bar", { mode: "all" })).rejects.toThrow(
"unknown column name"
);
@@ -469,8 +429,7 @@ describe("AnnoMatrixCrossfilter", () => {
});
test("addObsAnnoCategory", async () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let xfltr: any;
let xfltr;
// catch unknown or readonly columns
expect(() => crossfilter.addObsAnnoCategory("louvain", "mumble")).toThrow(
@@ -481,7 +440,6 @@ describe("AnnoMatrixCrossfilter", () => {
).toThrow("Unknown or readonly obs column");
// add a column and then add category to it
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: string; type: string; ca... Remove this comment to see the full error message
xfltr = await helperAddTestCol(crossfilter, "foo", {
name: "foo",
type: "categorical",
@@ -500,7 +458,6 @@ describe("AnnoMatrixCrossfilter", () => {
);
// now same, but ensure we have built an index before doing the operation
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: string; type: string; ca... Remove this comment to see the full error message
xfltr = await helperAddTestCol(crossfilter, "bar", {
name: "bar",
type: "categorical",
@@ -529,7 +486,6 @@ describe("AnnoMatrixCrossfilter", () => {
crossfilter.removeObsAnnoCategory("undefined-name", "mumble")
).rejects.toThrow("Unknown or readonly obs column");
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: string; type: string; ca... Remove this comment to see the full error message
xfltr = await helperAddTestCol(crossfilter, "foo", {
name: "foo",
type: "categorical",
@@ -539,8 +495,8 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
(await xfltr.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.every((v: any) => v === "unassigned")
.asArray()
.every((v) => v === "unassigned")
).toBeTruthy();
expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
name: "foo",
@@ -558,8 +514,8 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
(await xfltr1.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.every((v: any) => v === "unassigned")
.asArray()
.every((v) => v === "unassigned")
).toBeTruthy();
expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
name: "foo",
@@ -581,8 +537,8 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
(await xfltr2.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.every((v: any) => v === "red")
.asArray()
.every((v) => v === "red")
).toBeTruthy();
expect(xfltr2.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
name: "foo",
@@ -600,7 +556,6 @@ describe("AnnoMatrixCrossfilter", () => {
crossfilter.setObsColumnValues("undefined-name", [0], "mumble")
).rejects.toThrow("Unknown or readonly obs column");
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: string; type: string; ca... Remove this comment to see the full error message
let xfltr = await helperAddTestCol(crossfilter, "foo", {
name: "foo",
type: "categorical",
@@ -617,8 +572,8 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
(await xfltr.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.every((v: any) => v === "unassigned")
.asArray()
.every((v) => v === "unassigned")
).toBeTruthy();
const xfltr1 = await xfltr.setObsColumnValues("foo", [0, 10], "purple");
expect(
@@ -626,8 +581,7 @@ describe("AnnoMatrixCrossfilter", () => {
.col("foo")
.asArray()
.every(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(v: any, i: any) =>
(v, i) =>
v === "unassigned" || (v === "purple" && (i === 0 || i === 10))
)
).toBeTruthy();
@@ -661,7 +615,6 @@ describe("AnnoMatrixCrossfilter", () => {
crossfilter.resetObsColumnValues("undefined-name", "red", "blue")
).rejects.toThrow("Unknown or readonly obs column");
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: string; type: string; ca... Remove this comment to see the full error message
let xfltr = await helperAddTestCol(crossfilter, "foo", {
name: "foo",
type: "categorical",
@@ -685,22 +638,22 @@ describe("AnnoMatrixCrossfilter", () => {
expect(
(await xfltr1.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.filter((v: any) => v === "purple")
.asArray()
.filter((v) => v === "purple")
).toHaveLength(2);
xfltr1 = await xfltr1.resetObsColumnValues("foo", "purple", "magenta");
expect(
(await xfltr1.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.filter((v: any) => v === "magenta")
.asArray()
.filter((v) => v === "magenta")
).toHaveLength(2);
expect(
(await xfltr1.annoMatrix.fetch("obs", "foo"))
.col("foo")
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.filter((v: any) => v === "purple")
.asArray()
.filter((v) => v === "purple")
).toHaveLength(0);
expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
name: "foo",
@@ -720,16 +673,12 @@ describe("AnnoMatrixCrossfilter", () => {
describe("edge cases", () => {
test("transition from empty annoMatrix", async () => {
// select before fetch needs to work
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(fetch as any).once(
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
);
fetch.once(serverMocks.dataframeResponse(["louvain"], [obsLouvain]));
const xfltr = await crossfilter.select("obs", "louvain", {
mode: "exact",
values: "B cells",
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
expect((fetch as any).mock.calls).toHaveLength(1);
expect(fetch.mock.calls).toHaveLength(1);
expect(xfltr.obsCrossfilter.hasDimension("obs/louvain")).toBeTruthy();
expect(xfltr.obsCrossfilter.all()).toBe(xfltr.annoMatrix._cache.obs);
expect(xfltr.countSelected()).toEqual(
File diff suppressed because it is too large Load Diff
@@ -1,7 +1,6 @@
export const baseDataURL = "https://a.fake.url/api/v0.2";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(window as any).CELLXGENE = {
window.CELLXGENE = {
API: {
prefix: baseDataURL,
version: "v0.2/",
@@ -0,0 +1,211 @@
import { schema } from "./schema";
import { Dataframe, KeyIndex } from "../../../../src/util/dataframe";
import { encodeMatrixFBS } from "../../../../src/util/stateManager/matrix";
const indexedSchema = {
obsByName: Object.fromEntries(
schema.schema.annotations.obs.columns.map((v) => [v.name, v]) ?? []
),
varByName: Object.fromEntries(
schema.schema.annotations.var.columns.map((v) => [v.name, v]) ?? []
),
embByName: Object.fromEntries(
schema.schema.layout.obs.map((v) => [v.name, v]) ?? []
),
};
function makeMockColumn(s, length) {
const { type } = s;
switch (type) {
case "int32":
return new Int32Array(length).fill(Math.floor(99 * Math.random()));
case "string":
return new Array(length).fill("test");
case "float32":
return new Float32Array(length).fill(99 * Math.random());
case "boolean":
return new Array(length).fill(false);
case "categorical":
return new Array(length).fill(s.categories[0]);
default:
throw new Error("unkonwn type");
}
}
function getEncodedDataframe(colNames, length, colSchemas) {
const colIndex = new KeyIndex(colNames);
const columns = colSchemas.map((s) => makeMockColumn(s, length));
const df = new Dataframe([length, colNames.length], columns, null, colIndex);
const body = encodeMatrixFBS(df);
return body;
}
export function dataframeResponse(colNames, columns) {
const colIndex = new KeyIndex(colNames);
const df = new Dataframe(
[columns[0].length, colNames.length],
columns,
null,
colIndex
);
const body = encodeMatrixFBS(df);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return () => Promise.resolve({ body, init: { status: 200, headers } });
}
function annotationObsResponse(request) {
const url = new URL(request.url);
const params = Array.from(url.searchParams.entries());
const names = params
.filter(([k]) => k === "annotation-name")
.map(([, v]) => v);
if (!names.every((n) => indexedSchema.obsByName[n])) {
return Promise.reject(new Error("bad obs annotation name in URL"));
}
const colSchemas = names.map((n) => indexedSchema.obsByName[n]);
const body = getEncodedDataframe(
names,
schema.schema.dataframe.nObs,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
function annotationVarResponse(request) {
const url = new URL(request.url);
const params = Array.from(url.searchParams.entries());
const names = params
.filter(([k]) => k === "annotation-name")
.map(([, v]) => v);
if (!names.every((n) => indexedSchema.varByName[n])) {
return Promise.reject(new Error("bad var annotation name in URL"));
}
const colSchemas = names.map((n) => indexedSchema.varByName[n]);
const body = getEncodedDataframe(
names,
schema.schema.dataframe.nVar,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
function layoutObsResponse(request) {
const url = new URL(request.url);
const params = Array.from(url.searchParams.entries());
const names = params.filter(([k]) => k === "layout-name").map(([, v]) => v);
if (!names.every((n) => indexedSchema.embByName[n])) {
return Promise.reject(new Error("bad layout name in URL"));
}
const dims = names.map((n) => indexedSchema.embByName[n].dims).flat();
const colSchemas = names
.map((n) => [indexedSchema.embByName[n], indexedSchema.embByName[n]])
.flat();
const body = getEncodedDataframe(
dims,
schema.schema.dataframe.nObs,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
function dataVarResponse(request) {
const url = new URL(request.url);
const params = Array.from(url.searchParams.entries());
const colNames = params.map((v) => `${v[0]}/${v[1]}`);
const colSchemas = colNames.map(() => schema.schema.dataframe);
const body = getEncodedDataframe(
colNames,
schema.schema.dataframe.nObs,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
export function responder(request) {
const url = new URL(request.url);
const { pathname } = url;
if (pathname.endsWith("/annotations/obs")) {
return annotationObsResponse(request);
}
if (pathname.endsWith("/annotations/var")) {
return annotationVarResponse(request);
}
if (pathname.endsWith("/layout/obs")) {
return layoutObsResponse(request);
}
if (pathname.endsWith("/data/var")) {
return dataVarResponse(request);
}
return Promise.reject(new Error("bad URL"));
}
export function withExpected(expectedURL, expectedParams) {
/*
Do some additional error checking
*/
return (request) => {
// if URL is bogus, reject the promise
const url = new URL(request.url);
if (!url.pathname.endsWith(expectedURL)) {
return Promise.reject(new Error("Unexpected URL!"));
}
const params = Array.from(url.searchParams.entries()).sort(
(a, b) => a[0] < b[0]
);
expectedParams = expectedParams.slice().sort((a, b) => a[0] < b[0]);
if (
params.length !== expectedParams.length ||
!params.every(
(p, i) => p[0] === expectedParams[i][0] && p[1] === expectedParams[i][1]
)
) {
return Promise.reject(new Error("unexpected name requested in URL"));
}
return responder(request);
};
}
export function annotationsObs(names) {
return withExpected(
"/annotations/obs",
names.map((name) => ["annotation-name", name])
);
}
@@ -1,251 +0,0 @@
import { schema } from "./schema";
import { Dataframe, KeyIndex } from "../../../../src/util/dataframe";
import { encodeMatrixFBS } from "../../../../src/util/stateManager/matrix";
const indexedSchema = {
obsByName: Object.fromEntries(
schema.schema.annotations.obs.columns.map((v) => [v.name, v]) ?? []
),
varByName: Object.fromEntries(
schema.schema.annotations.var.columns.map((v) => [v.name, v]) ?? []
),
embByName: Object.fromEntries(
schema.schema.layout.obs.map((v) => [v.name, v]) ?? []
),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function makeMockColumn(s: any, length: any) {
const { type } = s;
switch (type) {
case "int32":
return new Int32Array(length).fill(Math.floor(99 * Math.random()));
case "string":
return new Array(length).fill("test");
case "float32":
return new Float32Array(length).fill(99 * Math.random());
case "boolean":
return new Array(length).fill(false);
case "categorical":
return new Array(length).fill(s.categories[0]);
default:
throw new Error("unkonwn type");
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function getEncodedDataframe(colNames: any, length: any, colSchemas: any) {
const colIndex = new KeyIndex(colNames);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const columns = colSchemas.map((s: any) => makeMockColumn(s, length));
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
const df = new Dataframe([length, colNames.length], columns, null, colIndex);
const body = encodeMatrixFBS(df);
return body;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function dataframeResponse(colNames: any, columns: any) {
const colIndex = new KeyIndex(colNames);
const df = new Dataframe(
[columns[0].length, colNames.length],
columns,
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
colIndex
);
const body = encodeMatrixFBS(df);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return () => Promise.resolve({ body, init: { status: 200, headers } });
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function annotationObsResponse(request: any) {
const url = new URL(request.url);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const params = Array.from((url.searchParams as any).entries());
const names = params
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
.filter(([k]) => k === "annotation-name")
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '([, v]: [any, any]) => any' is n... Remove this comment to see the full error message
.map(([, v]) => v);
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
if (!names.every((n) => indexedSchema.obsByName[n])) {
return Promise.reject(new Error("bad obs annotation name in URL"));
}
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
const colSchemas = names.map((n) => indexedSchema.obsByName[n]);
const body = getEncodedDataframe(
names,
schema.schema.dataframe.nObs,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function annotationVarResponse(request: any) {
const url = new URL(request.url);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const params = Array.from((url.searchParams as any).entries());
const names = params
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
.filter(([k]) => k === "annotation-name")
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '([, v]: [any, any]) => any' is n... Remove this comment to see the full error message
.map(([, v]) => v);
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
if (!names.every((n) => indexedSchema.varByName[n])) {
return Promise.reject(new Error("bad var annotation name in URL"));
}
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
const colSchemas = names.map((n) => indexedSchema.varByName[n]);
const body = getEncodedDataframe(
names,
schema.schema.dataframe.nVar,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function layoutObsResponse(request: any) {
const url = new URL(request.url);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const params = Array.from((url.searchParams as any).entries());
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
const names = params.filter(([k]) => k === "layout-name").map(([, v]) => v);
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
if (!names.every((n) => indexedSchema.embByName[n])) {
return Promise.reject(new Error("bad layout name in URL"));
}
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
const dims = names.map((n) => indexedSchema.embByName[n].dims).flat();
const colSchemas = names
// @ts-expect-error ts-migrate(2538) FIXME: Type 'unknown' cannot be used as an index type.
.map((n) => [indexedSchema.embByName[n], indexedSchema.embByName[n]])
.flat();
const body = getEncodedDataframe(
dims,
schema.schema.dataframe.nObs,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function dataVarResponse(request: any) {
const url = new URL(request.url);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const params = Array.from((url.searchParams as any).entries());
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const colNames = params.map((v) => `${(v as any)[0]}/${(v as any)[1]}`);
const colSchemas = colNames.map(() => schema.schema.dataframe);
const body = getEncodedDataframe(
colNames,
schema.schema.dataframe.nObs,
colSchemas
);
const headers = new Headers({
"Content-Type": "application/octet-stream",
});
return Promise.resolve({
body,
init: { status: 200, headers },
});
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function responder(request: any) {
const url = new URL(request.url);
const { pathname } = url;
if (pathname.endsWith("/annotations/obs")) {
return annotationObsResponse(request);
}
if (pathname.endsWith("/annotations/var")) {
return annotationVarResponse(request);
}
if (pathname.endsWith("/layout/obs")) {
return layoutObsResponse(request);
}
if (pathname.endsWith("/data/var")) {
return dataVarResponse(request);
}
return Promise.reject(new Error("bad URL"));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function withExpected(expectedURL: any, expectedParams: any) {
/*
Do some additional error checking
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
return (request: any) => {
// if URL is bogus, reject the promise
const url = new URL(request.url);
if (!url.pathname.endsWith(expectedURL)) {
return Promise.reject(new Error("Unexpected URL!"));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const params = Array.from((url.searchParams as any).entries()).sort(
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '(a: unknown, b: unknown) => bool... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(a, b) => (a as any)[0] < (b as any)[0]
);
expectedParams = expectedParams
.slice() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.sort((a: any, b: any) => a[0] < b[0]);
if (
params.length !== expectedParams.length ||
!params.every(
(p, i) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(p as any)[0] === expectedParams[i][0] && // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(p as any)[1] === expectedParams[i][1]
)
) {
return Promise.reject(new Error("unexpected name requested in URL"));
}
return responder(request);
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function annotationsObs(names: any) {
return withExpected(
"/annotations/obs",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
names.map((name: any) => ["annotation-name", name])
);
}
@@ -1,6 +1,4 @@
import { RawSchema } from "../../../../src/common/types/entities";
export const schema: { schema: RawSchema } = {
export const schema = {
schema: {
annotations: {
obs: {
File diff suppressed because it is too large Load Diff
@@ -218,18 +218,10 @@ describe("whereCache", () => {
},
})
);
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
expect((wc.where as any).field.queryField.has("queryColumn")).toEqual(true);
expect(wc.where.field.queryField.has("queryColumn")).toEqual(true);
expect(wc.where.field.queryField.get("queryColumn")).toBeInstanceOf(Map);
expect(
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(wc.where as any).field.queryField.get("queryColumn")
).toBeInstanceOf(Map);
expect(
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(wc.where as any).field.queryField.get("queryColumn").has("queryValue")
wc.where.field.queryField.get("queryColumn").has("queryValue")
).toEqual(true);
expect(_whereCacheGet(wc, schema, "field", query)).toEqual([0, 1, 2]);
});
@@ -8,12 +8,9 @@ import { indexEntireSchema } from "../../src/util/stateManager/schemaHelpers";
import { normalizeWritableCategoricalSchema } from "../../src/annoMatrix/normalize";
describe("centroid", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let schema: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let obsAnnotations: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let obsLayout: any;
let schema;
let obsAnnotations;
let obsLayout;
beforeAll(() => {
schema = indexEntireSchema(cloneDeep(REST.schema.schema));
@@ -47,8 +44,7 @@ describe("centroid", () => {
quantile([0.5], obsLayout.col("umap_1").asArray())[0],
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
centroidResult.forEach((coordinate: any) => {
centroidResult.forEach((coordinate) => {
expect(coordinate).toEqual(expectedResult);
});
});
@@ -72,8 +68,7 @@ describe("centroid", () => {
quantile([0.5], obsLayout.col("umap_1").asArray())[0],
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
centroidResult.forEach((coordinate: any) => {
centroidResult.forEach((coordinate) => {
expect(coordinate).toEqual(expectedResult);
});
});
@@ -29,7 +29,6 @@ describe("dataframe constructor", () => {
const df = new Dataframe.Dataframe(
[3, 2],
[new Int32Array([0, 1, 2]), new Int32Array([3, 4, 5])],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([2, 1, 0]),
new Dataframe.KeyIndex(["A", "B"])
);
@@ -56,7 +55,6 @@ describe("simple data access", () => {
new Float64Array([0.0, Number.NaN, Number.POSITIVE_INFINITY, 3.14159]),
["red", "blue", "green", "nan"],
],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([3, 2, 1, 0]),
new Dataframe.KeyIndex(["numbers", "colors"])
);
@@ -141,12 +139,10 @@ describe("dataframe subsetting", () => {
["red", "green", "blue"],
],
null, // identity index
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
);
test("all rows, one column", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfA = sourceDf.subset(null, ["colors"]);
expect(dfA).toBeDefined();
expect(dfA.dims).toEqual([3, 1]);
@@ -162,7 +158,6 @@ describe("dataframe subsetting", () => {
});
test("all rows, two columns", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfB = sourceDf.subset(null, ["float32", "colors"]);
expect(dfB).toBeDefined();
expect(dfB.dims).toEqual([3, 2]);
@@ -232,7 +227,6 @@ describe("dataframe subsetting", () => {
});
test("two rows, two colums", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfF = sourceDf.subset([0, 2], ["int32", "float32"]);
expect(dfF).toBeDefined();
expect(dfF.dims).toEqual([2, 2]);
@@ -242,7 +236,6 @@ describe("dataframe subsetting", () => {
expect(dfF.colIndex.labels()).toEqual(["int32", "float32"]);
// reverse the row and column order
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfFr = sourceDf.subset([2, 0], ["float32", "int32"]);
expect(dfFr).toBeDefined();
expect(dfFr.dims).toEqual([2, 2]);
@@ -255,7 +248,6 @@ describe("dataframe subsetting", () => {
test("withRowIndex", () => {
const df = sourceDf.subset(
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
["int32", "float32"],
new Dataframe.DenseInt32Index([3, 2, 1])
);
@@ -266,15 +258,12 @@ describe("dataframe subsetting", () => {
test("withRowIndex error checks", () => {
expect(() =>
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
sourceDf.subset(null, ["red"], new Dataframe.IdentityInt32Index(1))
).toThrow(RangeError);
expect(() =>
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
sourceDf.subset(null, ["red"], new Dataframe.DenseInt32Index([0, 1]))
).toThrow(RangeError);
expect(() =>
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
sourceDf.subset(null, ["red"], new Dataframe.KeyIndex([0, 1, 2, 3]))
).toThrow(RangeError);
});
@@ -289,14 +278,12 @@ describe("dataframe subsetting", () => {
new Float32Array([4.4, 5.5, 6.6]),
["red", "green", "blue"],
],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([2, 4, 6]),
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
);
const dfA = sourceDf.isubsetMask(
new Uint8Array([0, 1, 1]),
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'Uint8Array' is not assignable to... Remove this comment to see the full error message
new Uint8Array([1, 0, 0, 1])
);
expect(dfA.dims).toEqual([2, 2]);
@@ -316,7 +303,6 @@ describe("dataframe subsetting", () => {
["red", "green", "blue"],
],
null, // identity index
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
);
@@ -330,7 +316,6 @@ describe("dataframe subsetting", () => {
});
test("all rows, two cols", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message
const dfA = sourceDf.isubset(null, [1, 2]);
expect(dfA.dims).toEqual([3, 2]);
expect(dfA.icol(0).asArray()).toEqual(["A", "B", "C"]);
@@ -376,7 +361,6 @@ describe("dataframe factories", () => {
const dfA = new Dataframe.Dataframe(
[3, 2],
[new Int32Array([0, 1, 2]), new Int32Array([3, 4, 5])],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([2, 1, 0]),
new Dataframe.KeyIndex(["A", "B"])
);
@@ -401,7 +385,6 @@ describe("dataframe factories", () => {
[true, false],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools"])
);
const dfA = df.withCol("numbers", [1, 0]);
@@ -425,7 +408,6 @@ describe("dataframe factories", () => {
[true, false],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([74, 75])
);
const dfA = df.withCol(72, [1, 0]);
@@ -451,7 +433,6 @@ describe("dataframe factories", () => {
[true, false],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([74, 75])
);
const dfA = df.withCol(999, [1, 0]);
@@ -560,7 +541,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
@@ -569,14 +549,11 @@ describe("dataframe factories", () => {
[3, 1],
[["red", "blue", "green"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colorsA"])
);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
expect(() => dfA.withColsFrom(dfB)).toThrow(RangeError);
/* duplicate labels should throw an error */
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
expect(() => dfA.withColsFrom(dfA)).toThrow(Error);
});
@@ -587,18 +564,15 @@ describe("dataframe factories", () => {
[2, 1],
[["red", "blue"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors"])
);
const dfB = new Dataframe.Dataframe(
[2, 1],
[[true, false]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["bools"])
);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const dfLikeA = dfEmpty.withColsFrom(dfA);
expect(dfLikeA).toBeDefined();
expect(dfLikeA.dims).toEqual(dfA.dims);
@@ -607,7 +581,6 @@ describe("dataframe factories", () => {
expect(dfLikeA.rowIndex.labels()).toEqual(dfA.rowIndex.labels());
expect(dfLikeA.icol(0).asArray()).toEqual(dfA.icol(0).asArray());
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const dfAlsoLikeA = dfA.withColsFrom(dfEmpty);
expect(dfAlsoLikeA).toBeDefined();
expect(dfAlsoLikeA.dims).toEqual(dfA.dims);
@@ -616,7 +589,6 @@ describe("dataframe factories", () => {
expect(dfAlsoLikeA.rowIndex.labels()).toEqual(dfA.rowIndex.labels());
expect(dfAlsoLikeA.icol(0).asArray()).toEqual(dfA.icol(0).asArray());
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const dfC = dfA.withColsFrom(dfB);
expect(dfC).toBeDefined();
expect(dfC.dims).toEqual([2, 2]);
@@ -633,7 +605,6 @@ describe("dataframe factories", () => {
[2, 1],
[["red", "blue"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors"])
);
const dfB = new Dataframe.Dataframe(
@@ -644,7 +615,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
@@ -677,7 +647,6 @@ describe("dataframe factories", () => {
[2, 1],
[["red", "blue"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors"])
);
const dfB = new Dataframe.Dataframe(
@@ -688,7 +657,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
@@ -712,7 +680,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
const dfA = df.dropCol("colors");
@@ -784,7 +751,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([102, 101, 100])
);
const dfA = df.dropCol(101);
@@ -811,8 +777,7 @@ describe("dataframe factories", () => {
new Float64Array(3).fill(1.1),
]
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const dfB = dfA.mapColumns((col: any, idx: any) => {
const dfB = dfA.mapColumns((col, idx) => {
expect(dfA.icol(idx).asArray()).toBe(col);
return col;
});
@@ -855,7 +820,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["A", "B"])
);
const dfB = dfA.renameCol("B", "C");
@@ -868,8 +832,7 @@ describe("dataframe factories", () => {
});
describe("dataframe col", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let df: any = null;
let df = null;
beforeEach(() => {
df = new Dataframe.Dataframe(
[2, 2],
@@ -878,7 +841,6 @@ describe("dataframe col", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["A", "B"])
);
});
@@ -1231,7 +1193,6 @@ describe("label indexing", () => {
test("create", () => {
expect(Dataframe.isLabelIndex(idx)).toBeTruthy();
expect(() => new Dataframe.KeyIndex(["dup", "dup"])).toThrow(Error);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
expect(new Dataframe.KeyIndex().size()).toEqual(0);
});
@@ -1404,7 +1365,6 @@ describe("corner cases", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["A", "B"])
);
@@ -6,7 +6,6 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
@@ -27,7 +26,6 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
@@ -50,7 +48,6 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
@@ -71,7 +68,6 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
@@ -92,16 +88,7 @@ describe("Dataframe column histogram", () => {
expect(df.col(1).histogram(5, [0, 100])).toEqual([5, 1, 0, 0, 2]);
expect(df.col(0).histogram(2, [0, 10])).toEqual([2, 2]);
expect(df.col(0).histogram(10, [0, 100])).toEqual([
3,
2,
1,
0,
0,
0,
0,
0,
0,
2,
3, 2, 1, 0, 0, 0, 0, 0, 0, 2,
]);
});
});
@@ -1,7 +1,6 @@
import * as Dataframe from "../../../src/util/dataframe";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function float32Conversion(f: any) {
function float32Conversion(f) {
return new Float32Array([f])[0];
}
@@ -31,7 +30,6 @@ describe("Dataframe column summary", () => {
[1],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex([
"name",
"nameString",
@@ -108,7 +106,6 @@ describe("Dataframe column summary", () => {
[1, false, "0"],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex([
"name",
"nameString",
@@ -177,7 +174,6 @@ describe("Dataframe column summary", () => {
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([1, false, "0"]),
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
categoryCounts: new Map([
[1, 1],
[false, 1],
@@ -205,7 +201,6 @@ describe("Dataframe column summary", () => {
[1, false, "0", "0"],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex([
"name",
"nameString",
@@ -274,7 +269,6 @@ describe("Dataframe column summary", () => {
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([1, false, "0"]),
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
categoryCounts: new Map([
[1, 1],
[false, 1],
@@ -1,8 +1,7 @@
import PromiseLimit from "../../src/util/promiseLimit";
import { range } from "../../src/util/range";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const delay = (t: any) => new Promise((resolve) => setTimeout(resolve, t));
const delay = (t) => new Promise((resolve) => setTimeout(resolve, t));
describe("PromiseLimit", () => {
test("simple evaluation, concurrency 1", async () => {
@@ -52,9 +51,7 @@ describe("PromiseLimit", () => {
running -= 1;
};
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 1.
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
await Promise.all(range(10).map((i: any) => plimit.add(() => callback(i))));
await Promise.all(range(10).map((i) => plimit.add(() => callback(i))));
expect(maxRunning).toEqual(2);
});
@@ -19,11 +19,7 @@ describe("quantile", () => {
test("multi q", () => {
const arr = new Float32Array([9, 3, 5, 6, 0]);
expect(quantile([0, 0.25, 0.5, 0.75, 1.0], arr)).toMatchObject([
0,
3,
5,
6,
9,
0, 3, 5, 6, 9,
]);
});
});
@@ -6,20 +6,14 @@ describe("range", () => {
});
test("range(stop)", () => {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 1.
expect(range(3)).toMatchObject([0, 1, 2]);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 1.
expect(range(0)).toMatchObject([]);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 1.
expect(range(1)).toMatchObject([0]);
});
test("range(start,stop)", () => {
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2.
expect(range(0, 0)).toMatchObject([]);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2.
expect(range(0, 2)).toMatchObject([0, 1]);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 3 arguments, but got 2.
expect(range(4, 8)).toMatchObject([4, 5, 6, 7]);
});
@@ -81,7 +81,6 @@ describe("categorical color helpers", () => {
),
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["continuousColumn", "categoricalColumn"])
);
@@ -96,7 +95,6 @@ describe("categorical color helpers", () => {
const data = obsDataframe.col("categoricalColumn").asArray();
const cats = schema.annotations.obsByName.categoricalColumn.categories;
for (let i = 0; i < schema.dataframe.nObs; i += 1) {
// @ts-expect-error ts-migrate(2722) FIXME: Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message
expect(makeScale(ct.rgb[i])).toEqual(ct.scale(cats.indexOf(data[i])));
}
});
@@ -114,7 +112,6 @@ describe("categorical color helpers", () => {
const data = obsDataframe.col("categoricalColumn").asArray();
const cats = schemaClone.annotations.obsByName.categoricalColumn.categories;
for (let i = 0; i < schemaClone.dataframe.nObs; i += 1) {
// @ts-expect-error ts-migrate(2722) FIXME: Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message
expect(makeScale(ct.rgb[i])).toEqual(ct.scale(cats.indexOf(data[i])));
}
});
@@ -125,8 +122,7 @@ describe("categorical color helpers", () => {
Array.from(schema.annotations.obsByName.categoricalColumn.categories)
);
const userDefinedColorTable = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoricalColumn: shuffleCats.reduce((acc: any, label: any) => {
categoricalColumn: shuffleCats.reduce((acc, label) => {
acc[label] = randRGBColor();
return acc;
}, {}),
@@ -140,14 +136,12 @@ describe("categorical color helpers", () => {
"categoricalColumn",
obsDataframe,
schema,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{}' is not assignable to paramet... Remove this comment to see the full error message
userColors
);
expect(ct).toBeDefined();
const data = obsDataframe.col("categoricalColumn").asArray();
for (let i = 0; i < schema.dataframe.nObs; i += 1) {
expect(makeScale(ct.rgb[i])).toEqual(
// @ts-expect-error ts-migrate(2722) FIXME: Cannot invoke an object which is possibly 'undefin... Remove this comment to see the full error message
ct.scale(cats.indexOf(data[i])).toString()
);
}
@@ -160,38 +154,31 @@ TODO:
2. user defined colors
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function indexSchema(schema: any) {
function indexSchema(schema) {
schema.annotations.obsByName = Object.fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
schema.annotations?.obs?.columns?.map((v: any) => [v.name, v]) ?? []
schema.annotations?.obs?.columns?.map((v) => [v.name, v]) ?? []
);
schema.annotations.varByName = Object.fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
schema.annotations?.var?.columns?.map((v: any) => [v.name, v]) ?? []
schema.annotations?.var?.columns?.map((v) => [v.name, v]) ?? []
);
schema.layout.obsByName = Object.fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
schema.layout?.obs?.map((v: any) => [v.name, v]) ?? []
schema.layout?.obs?.map((v) => [v.name, v]) ?? []
);
schema.layout.varByName = Object.fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
schema.layout?.var?.map((v: any) => [v.name, v]) ?? []
schema.layout?.var?.map((v) => [v.name, v]) ?? []
);
return schema;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function makeScale(rgb: any) {
function makeScale(rgb) {
// make a scale string from a rgb float triple
return `rgb(${(rgb[0] * 255) >>> 0}, ${(rgb[1] * 255) >>> 0}, ${
(rgb[2] * 256) >>> 0
})`;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function shuffle(array: any) {
function shuffle(array) {
for (let i = array.length - 1; i > 0; i -= 1) {
const j = (Math.random() * (i + 1)) >>> 0;
[array[i], array[j]] = [array[j], array[i]];
@@ -24,7 +24,6 @@ describe("encode/decode", () => {
expect(dfA.columns).toEqual(columns);
const colIndex = new KeyIndex(["a", "b", "c", "d"]);
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
const dfWithColIdx = new Dataframe([3, 4], columns, null, colIndex);
const dfB = decodeMatrixFBS(encodeMatrixFBS(dfWithColIdx));
expect([dfB.nRows, dfB.nCols]).toEqual(dfWithColIdx.dims);
@@ -5,7 +5,6 @@ import zip from "lodash.zip";
import _ from "lodash";
import { flatbuffers } from "flatbuffers";
import { NetEncoding } from "../../../src/util/stateManager/matrix_generated";
import { RawSchema } from "../../../src/common/types/entities";
/*
test data mocking REST 0.2 API responses. Used in several tests.
@@ -30,7 +29,7 @@ const aConfigResponse = {
},
};
const aSchemaResponse: { schema: RawSchema } = {
const aSchemaResponse = {
schema: {
dataframe: {
nObs,
@@ -41,30 +40,28 @@ const aSchemaResponse: { schema: RawSchema } = {
obs: {
index: "name",
columns: [
{ name: "name", type: "string", writable: false },
{ name: "field1", type: "int32", writable: false },
{ name: "field2", type: "float32", writable: false },
{ name: "field3", type: "boolean", writable: false },
{ name: "name", type: "string" },
{ name: "field1", type: "int32" },
{ name: "field2", type: "float32" },
{ name: "field3", type: "boolean" },
{
name: "field4",
type: "categorical",
categories: field4Categories,
writable: false,
},
],
},
var: {
index: "name",
columns: [
{ name: "name", type: "string", writable: false },
{ name: "fieldA", type: "int32", writable: false },
{ name: "fieldB", type: "float32", writable: false },
{ name: "fieldC", type: "boolean", writable: false },
{ name: "name", type: "string" },
{ name: "fieldA", type: "int32" },
{ name: "fieldB", type: "float32" },
{ name: "fieldC", type: "boolean" },
{
name: "fieldD",
type: "categorical",
categories: fieldDCategories,
writable: false,
},
],
},
@@ -78,7 +75,6 @@ const aSchemaResponse: { schema: RawSchema } = {
const anAnnotationsObsJSONResponse = {
names: ["name", "field1", "field2", "field3", "field4"],
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
data: _()
.range(nObs)
.map((idx) => [
@@ -95,7 +91,6 @@ const anAnnotationsObsJSONResponse = {
const anAnnotationsVarJSONResponse = {
names: ["fieldA", "fieldB", "fieldC", "fieldD", "name"],
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
data: _()
.range(nVar)
.map((idx) => [
@@ -110,11 +105,8 @@ const anAnnotationsVarJSONResponse = {
.value(),
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function encodeTypedArray(builder: any, uType: any, uData: any) {
// @ts-expect-error --- FIXME: Element implicitly has an 'any' type.
function encodeTypedArray(builder, uType, uData) {
const uTypeName = NetEncoding.TypedArray[uType];
// @ts-expect-error --- FIXME: Element implicitly has an 'any' type.
const ArrayType = NetEncoding[uTypeName];
const dv = ArrayType.createDataVector(builder, uData);
builder.startObject(1);
@@ -122,8 +114,7 @@ function encodeTypedArray(builder: any, uType: any, uData: any) {
return builder.endObject();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function encodeMatrix(columns: any, colIndex = undefined) {
function encodeMatrix(columns, colIndex = undefined) {
/*
IMPORTANT: this is not a general purpose encoder. in particular,
it doesn't correctly handle all column index types, nor does it
@@ -132,7 +123,6 @@ function encodeMatrix(columns: any, colIndex = undefined) {
encodeMatrixFBS in matrix.py is more general. This is used only
as a testing santity check (alt implementation).
*/
// @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 1.
const utf8Encoder = new TextEncoder("utf-8");
const builder = new flatbuffers.Builder(1024);
const cols = map(columns, (carr) => {
@@ -182,13 +172,11 @@ function encodeMatrix(columns: any, colIndex = undefined) {
const anAnnotationsObsFBSResponse = (() => {
const columns = zip(...anAnnotationsObsJSONResponse.data).slice(1);
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
return encodeMatrix(columns, anAnnotationsObsJSONResponse.names);
})();
const anAnnotationsVarFBSResponse = (() => {
const columns = zip(...anAnnotationsVarJSONResponse.data).slice(1);
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
return encodeMatrix(columns, anAnnotationsVarJSONResponse.names);
})();
@@ -197,13 +185,11 @@ const aLayoutFBSResponse = (() => {
new Float32Array(nObs).fill(Math.random()),
new Float32Array(nObs).fill(Math.random()),
];
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
return encodeMatrix(coords, ["umap_0", "umap_1"]);
})();
const aDataObsResponse = {
var: [2, 4, 29],
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
obs: _()
.range(nObs)
.map((idx) => [idx, Math.random(), Math.random(), Math.random()])
@@ -126,8 +126,7 @@ const someData = [
},
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let payments: any = null;
let payments = null;
beforeEach(() => {
payments = new Crossfilter(someData);
});
@@ -139,13 +138,7 @@ describe("ImmutableTypedCrossfilter", () => {
expect(payments.all()).toEqual(someData);
const p = payments
.addDimension(
"quantity",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].quantity,
Int32Array
)
.addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array)
.select("quantity", { mode: "all" });
expect(p).toBeDefined();
expect(p.all()).toEqual(someData);
@@ -165,8 +158,7 @@ describe("ImmutableTypedCrossfilter", () => {
const p2 = payments.addDimension(
"quantity",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, data: any) => data[i].quantity,
(i, data) => data[i].quantity,
Int32Array
);
@@ -183,22 +175,10 @@ describe("ImmutableTypedCrossfilter", () => {
test("select all and none", () => {
let p = payments
.addDimension(
"quantity",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].quantity,
Int32Array
) // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.addDimension("tip", "scalar", (i: any, d: any) => d[i].tip, Float32Array)
.addDimension(
"total",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].total,
Float32Array
) // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.addDimension("type", "enum", (i: any, d: any) => d[i].type);
.addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array)
.addDimension("tip", "scalar", (i, d) => d[i].tip, Float32Array)
.addDimension("total", "scalar", (i, d) => d[i].total, Float32Array)
.addDimension("type", "enum", (i, d) => d[i].type);
expect(p).toBeDefined();
/* expect all records to be selected - default init state */
@@ -250,24 +230,11 @@ describe("ImmutableTypedCrossfilter", () => {
});
describe("scalar dimension", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let p: any;
let p;
beforeEach(() => {
p = payments
.addDimension(
"quantity",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].quantity,
Int32Array
)
.addDimension(
"tip",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].tip,
Float32Array
)
.addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array)
.addDimension("tip", "scalar", (i, d) => d[i].tip, Float32Array)
.select("tip", { mode: "all" });
});
@@ -310,11 +277,9 @@ describe("ImmutableTypedCrossfilter", () => {
});
describe("enum dimension", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let p: any;
let p;
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
p = payments.addDimension("type", "enum", (i: any, d: any) => d[i].type);
p = payments.addDimension("type", "enum", (i, d) => d[i].type);
});
test("all", () => {
@@ -352,8 +317,7 @@ describe("ImmutableTypedCrossfilter", () => {
});
describe("spatial dimension", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let p: any;
let p;
beforeEach(() => {
const X = someData.map((r) => r.coords[0]);
const Y = someData.map((r) => r.coords[1]);
@@ -442,22 +406,14 @@ describe("ImmutableTypedCrossfilter", () => {
});
describe("non-finite scalars", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let p: any;
let p;
beforeEach(() => {
p = payments
.addDimension(
"quantity",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].quantity,
Int32Array
)
.addDimension("quantity", "scalar", (i, d) => d[i].quantity, Int32Array)
.addDimension(
"nonFinite",
"scalar",
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(i: any, d: any) => d[i].nonFinite,
(i, d) => d[i].nonFinite,
Float32Array
)
.select("quantity", { mode: "all" });
@@ -151,9 +151,9 @@ describe("intersection", () => {
[1, 2],
[6, 9],
]);
expect(
PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]])
).toEqual([[1363, 2638]]);
expect(PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]])).toEqual(
[[1363, 2638]]
);
expect(PositiveIntervals.intersection([[1, 2]], [[1, 2]])).toEqual([
[1, 2],
]);
@@ -15,8 +15,7 @@ paths for:
const pInf = Number.POSITIVE_INFINITY;
const nInf = Number.NEGATIVE_INFINITY;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function fillRange(arr: any, start = 0) {
function fillRange(arr, start = 0) {
const larr = arr;
for (let i = 0, len = larr.length; i < len; i += 1) {
larr[i] = i + start;
@@ -24,8 +23,7 @@ function fillRange(arr: any, start = 0) {
return larr;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function fillRand(arr: any) {
function fillRand(arr) {
for (let i = 0, len = arr.length; i < len; i += 1) {
arr[i] = Math.random();
}
@@ -50,22 +48,16 @@ describe("sortArray", () => {
describe("finite numbers", () => {
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map((Type) =>
test(Type.name, () => {
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
expect(sortArray(Type.from([6, 5, 4, 3, 2, 1, 0]))).toMatchObject(
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
Type.from([0, 1, 2, 3, 4, 5, 6])
);
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
expect(sortArray(Type.from([6, 5, 4, 3, 2, 1]))).toMatchObject(
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
Type.from([1, 2, 3, 4, 5, 6])
);
const source = fillRand(new Type(1000));
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
expect(sortArray(Type.from(source))).toMatchObject(
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
Type.from(source).sort()
);
})
@@ -138,27 +130,22 @@ describe("sortIndex", () => {
describe("finite numbers", () => {
[Array, Float32Array, Uint32Array, Int32Array, Float64Array].map((Type) =>
test(Type.name, () => {
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
const source1 = Type.from([6, 5, 4, 3, 2, 1, 0]);
const index1 = fillRange(new Uint32Array(source1.length));
expect(sortIndex(index1, source1)).toMatchObject(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
index1.sort((a: any, b: any) => source1[a] - source1[b])
index1.sort((a, b) => source1[a] - source1[b])
);
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
const source2 = Type.from([6, 5, 4, 3, 2, 1]);
const index2 = fillRange(new Uint32Array(source2.length));
expect(sortIndex(index2, source2)).toMatchObject(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
index2.sort((a: any, b: any) => source1[a] - source1[b])
index2.sort((a, b) => source1[a] - source1[b])
);
const source3 = fillRand(new Type(1000));
const index3 = fillRange(new Uint32Array(source3.length));
expect(sortIndex(index3, source3)).toMatchObject(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
index3.sort((a: any, b: any) => source1[a] - source1[b])
index3.sort((a, b) => source1[a] - source1[b])
);
})
);
+1 -1
View File
@@ -11,13 +11,13 @@ module.exports = {
},
],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
"@babel/plugin-proposal-function-bind",
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
["@babel/plugin-proposal-private-methods", { loose: true }],
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
+1 -1
View File
@@ -10,13 +10,13 @@ module.exports = {
},
],
"@babel/preset-react",
"@babel/preset-typescript",
],
plugins: [
"@babel/plugin-proposal-function-bind",
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
["@babel/plugin-proposal-private-methods", { loose: true }],
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-transform-react-constant-elements",
"@babel/plugin-transform-runtime",
+8 -37
View File
@@ -1,10 +1,8 @@
/* eslint-disable @blueprintjs/classes-constants -- we don't import blueprint here */
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
extends: [
"airbnb-typescript",
"plugin:@typescript-eslint/recommended",
"airbnb",
"plugin:eslint-comments/recommended",
"plugin:@blueprintjs/recommended",
"plugin:compat/recommended",
@@ -30,6 +28,7 @@ module.exports = {
context: true,
beforeEach: true,
},
parser: "@babel/eslint-parser",
parserOptions: {
ecmaVersion: 2017,
sourceType: "module",
@@ -37,47 +36,21 @@ module.exports = {
jsx: true,
generators: true,
},
project: "./tsconfig.json",
babelOptions: {
configFile: "./configuration/babel/babel.prod.js",
},
},
rules: {
"react/jsx-no-target-blank": "off",
"eslint-comments/require-description": ["error"],
"no-magic-numbers": "off",
"@typescript-eslint/no-magic-numbers": "off",
"no-nested-ternary": "off",
"func-style": "off",
"arrow-parens": "off",
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": "off",
"react/jsx-filename-extension": "off",
"comma-dangle": "off",
"@typescript-eslint/comma-dangle": "off",
"no-underscore-dangle": "off",
// Override airbnb config to allow leading underscore
// https://github.com/iamturns/eslint-config-airbnb-typescript/blob/master/lib/shared.js#L35
"@typescript-eslint/naming-convention": [
"error",
{
selector: "class",
format: ["PascalCase"],
leadingUnderscore: "allow",
},
{
selector: "function",
format: ["camelCase", "PascalCase"],
leadingUnderscore: "allowSingleOrDouble",
},
{
selector: "typeLike",
format: ["PascalCase"],
},
{
selector: "variable",
format: ["camelCase", "PascalCase", "UPPER_CASE"],
leadingUnderscore: "allowSingleOrDouble",
trailingUnderscore: "allowDouble",
},
],
"implicit-arrow-linebreak": "off",
"no-console": "off",
"spaced-comment": ["error", "always", { exceptions: ["*"] }],
@@ -85,7 +58,6 @@ module.exports = {
"object-curly-newline": ["error", { consistent: true }],
"react/prop-types": [0],
"space-before-function-paren": "off",
"@typescript-eslint/space-before-function-paren": "off",
"function-paren-newline": "off",
"prefer-destructuring": ["error", { object: true, array: false }],
"import/prefer-default-export": "off",
@@ -104,9 +76,9 @@ module.exports = {
},
overrides: [
{
files: ["**/*.test.ts"],
files: ["**/*.test.js"],
env: {
jest: true, // now **/*.test.ts files' env has both es6 *and* jest
jest: true, // now **/*.test.js files' env has both es6 *and* jest
},
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
// "extends": ["plugin:jest/recommended"]
@@ -121,4 +93,3 @@ module.exports = {
},
],
};
/* eslint-enable @blueprintjs/classes-constants -- we don't import blueprint here */
@@ -1,4 +1,4 @@
module.exports = {
"*.{js,ts,jsx,tsx}": "eslint --fix",
"*.js": "eslint --fix",
"**/*": "prettier --write --ignore-unknown",
};
@@ -1,10 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies -- this file is a devDependency*/
/* eslint-disable @blueprintjs/classes-constants -- we don't import blueprint here */
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const cheerio = require("cheerio");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const crypto = require("crypto");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const HtmlWebpackPlugin = require("html-webpack-plugin");
const digest = (str) => {
@@ -54,5 +50,4 @@ class CspHashPlugin {
}
module.exports = CspHashPlugin;
/* eslint-enable @blueprintjs/classes-constants -- we don't import blueprint here */
/* eslint-enable import/no-extraneous-dependencies -- enable*/
@@ -22,7 +22,7 @@
>
<img
src="https://cellxgene.cziscience.com/s3/cellxgene/static/images/cellxgene-logo.png"
style="width: 320px"
style="width: 320px;"
/>
<div
style="
@@ -37,34 +37,36 @@
max-width: 550px;
"
>
<div style="margin-bottom: 0; font-weight: bolder; font-size: 1.2em">
<div style="margin-bottom: 0; font-weight: bolder; font-size: 1.2em;">
Unsupported Browser
</div>
<div style="margin-top: 0">
<div style="margin-top: 0;">
cellxgene is currently supported on the following browsers
</div>
<div style="display: flex; justify-content: space-around; margin-top: 16px">
<div
style="display: flex; justify-content: space-around; margin-top: 16px;"
>
<a
href="https://www.google.com/chrome/?hl=en%22"
aria-label="Download Google Chrome"
>
<img
src="https://cellxgene.cziscience.com/s3/cellxgene/static/images/chrome.png"
style="width: 80px; height: 80px"
style="width: 80px; height: 80px;"
/>
<div>Chrome &gt; 60</div>
</a>
<a href="https://www.mozilla.com/firefox/" aria-label="Download Firefox">
<img
src="https://cellxgene.cziscience.com/s3/cellxgene/static/images/firefox.png"
style="width: 80px; height: 80px"
style="width: 80px; height: 80px;"
/>
<div>Firefox ≥ 60</div>
</a>
<a href="//www.microsoft.com/edge" aria-label="Download Edge">
<img
src="https://cellxgene.cziscience.com/s3/cellxgene/static/images/edge.png"
style="width: 80px; height: 80px"
style="width: 80px; height: 80px;"
/>
<div>Edge ≥ 79</div>
</a>
@@ -1,23 +1,13 @@
/* eslint-disable @blueprintjs/classes-constants -- we don't import blueprint here */
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const path = require("path");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const webpack = require("webpack");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const HtmlWebpackPlugin = require("html-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const { merge } = require("webpack-merge");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const sharedConfig = require("./webpack.config.shared");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const babelOptions = require("../babel/babel.dev");
const fonts = path.resolve("src/fonts");
@@ -33,7 +23,7 @@ const devConfig = {
module: {
rules: [
{
test: /\.(ts|js)x?$/,
test: /\.jsx?$/,
loader: "babel-loader",
options: babelOptions,
},
@@ -93,4 +83,3 @@ const devConfig = {
};
module.exports = merge(sharedConfig, devConfig);
/* eslint-enable @blueprintjs/classes-constants -- we don't import blueprint here */
@@ -1,31 +1,18 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const path = require("path");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const webpack = require("webpack");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const HtmlWebpackPlugin = require("html-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const TerserJSPlugin = require("terser-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const CleanCss = require("clean-css");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const FaviconsWebpackPlugin = require("favicons-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const { merge } = require("webpack-merge");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const babelOptions = require("../babel/babel.prod");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const CspHashPlugin = require("./cspHashPlugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const sharedConfig = require("./webpack.config.shared");
const fonts = path.resolve("src/fonts");
@@ -51,7 +38,7 @@ const prodConfig = {
module: {
rules: [
{
test: /\.(ts|js)x?$/,
test: /\.jsx?$/,
loader: "babel-loader",
options: babelOptions,
},
@@ -1,13 +1,8 @@
/* eslint-disable @blueprintjs/classes-constants -- we don't import blueprint here */
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const path = require("path");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const fs = require("fs");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
const ObsoleteWebpackPlugin = require("obsolete-webpack-plugin");
// eslint-disable-next-line @typescript-eslint/no-var-requires --- FIXME: disabled temporarily on migrate to TS.
// eslint-disable-next-line @blueprintjs/classes-constants -- incorrect match
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
const src = path.resolve("src");
@@ -34,9 +29,6 @@ module.exports = {
path: path.resolve("build"),
publicPath,
},
resolve: {
extensions: [".ts", ".tsx", "..."],
},
module: {
rules: [
{
@@ -80,4 +72,3 @@ module.exports = {
}),
],
};
/* eslint-enable @blueprintjs/classes-constants -- we don't import blueprint here */
+6150 -4403
View File
File diff suppressed because it is too large Load Diff
+22 -48
View File
@@ -1,6 +1,6 @@
{
"name": "cellxgene",
"version": "0.17.0",
"version": "0.18.0",
"license": "MIT",
"description": "cellxgene is a web application for the interactive exploration of single cell sequence data.",
"repository": "https://github.com/chanzuckerberg/cellxgene",
@@ -8,14 +8,13 @@
"build": "npm run clean && webpack --config",
"clean": "rimraf build",
"dev": "npm run build -- configuration/webpack/webpack.config.dev.js",
"e2e": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.ts",
"e2e-annotations": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2eAnnotations.test.ts",
"e2e-prod": "CXG_URL_BASE='https://cellxgene.cziscience.com/d/pbmc3k.cxg/' jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.ts",
"e2e": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.js",
"e2e-annotations": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2eAnnotations.test.js",
"e2e-prod": "CXG_URL_BASE='https://cellxgene.cziscience.com/d/pbmc3k.cxg/' jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.js",
"fmt": "eslint --fix src __tests__",
"lint": "eslint src __tests__ & npm run type-check",
"lint": "eslint --fix src __tests__",
"prod": "npm run build -- configuration/webpack/webpack.config.prod.js",
"test": "jest --testPathIgnorePatterns e2e",
"type-check": "tsc --noEmit"
"test": "jest --testPathIgnorePatterns e2e"
},
"engineStrict": true,
"engines": {
@@ -40,12 +39,13 @@
],
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.13.0",
"@babel/eslint-parser": "^7.15.0",
"@blueprintjs/core": "^3.44.0",
"@blueprintjs/icons": "^3.19.0",
"@blueprintjs/popover2": "^0.6.0",
"@blueprintjs/popover2": "^0.11.2",
"@blueprintjs/select": "^3.16.0",
"abort-controller": "^3.0.0",
"core-js": "^3.6.5",
"core-js": "^3.16.3",
"d3": "^4.10.0",
"d3-scale-chromatic": "^1.5.0",
"flatbuffers": "^1.11.0",
@@ -89,39 +89,10 @@
"@babel/plugin-transform-runtime": "^7.13.15",
"@babel/preset-env": "^7.13.15",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.14.5",
"@babel/register": "^7.13.16",
"@babel/runtime": "^7.13.16",
"@blueprintjs/eslint-plugin": "^0.3.0",
"@sentry/webpack-plugin": "^1.15.0",
"@types/d3": "^7.0.0",
"@types/d3-scale-chromatic": "^3.0.0",
"@types/expect-puppeteer": "^4.4.6",
"@types/flatbuffers": "^1.10.0",
"@types/is-number": "^7.0.1",
"@types/jest": "^26.0.24",
"@types/jest-environment-puppeteer": "^4.4.1",
"@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.difference": "^4.5.6",
"@types/lodash.every": "^4.6.6",
"@types/lodash.filter": "^4.6.6",
"@types/lodash.foreach": "^4.5.6",
"@types/lodash.isnumber": "^3.0.6",
"@types/lodash.map": "^4.6.13",
"@types/lodash.pull": "^4.1.6",
"@types/lodash.sortby": "^4.7.6",
"@types/lodash.uniq": "^4.5.6",
"@types/lodash.zip": "^4.2.6",
"@types/pako": "^1.0.2",
"@types/puppeteer": "^5.4.4",
"@types/react": "^17.0.14",
"@types/react-dom": "^17.0.9",
"@types/react-helmet": "^6.1.2",
"@types/react-redux": "^7.1.18",
"@types/sha1": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^4.28.4",
"@typescript-eslint/parser": "^4.28.4",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.1.0",
"babel-loader": "^8.1.0",
"babel-preset-modern-browsers": "^15.0.2",
@@ -132,13 +103,12 @@
"codecov": "^3.7.1",
"css-loader": "^5.2.4",
"eslint": "^7.24.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint-config-airbnb": "^18.2.0",
"eslint-config-prettier": "^8.2.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-compat": "^3.8.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-jest": "^24.3.5",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.23.2",
@@ -150,8 +120,8 @@
"file-loader": "^6.0.0",
"html-webpack-plugin": "^5.3.1",
"husky": "^4.2.5",
"jest": "^26.1.0",
"jest-circus": "^26.1.0",
"jest": "^27.0.6",
"jest-circus": "^27.0.6",
"jest-environment-puppeteer": "^5.0.1",
"jest-fetch-mock": "^3.0.3",
"jest-puppeteer": "^5.0.1",
@@ -172,7 +142,6 @@
"script-ext-html-webpack-plugin": "^2.1.4",
"serve-favicon": "^2.5.0",
"terser-webpack-plugin": "^5.1.1",
"typescript": "^4.3.5",
"webpack": "^5.34.0",
"webpack-cli": "^4.6.0",
"webpack-dev-middleware": "^4.1.0",
@@ -180,10 +149,10 @@
},
"jest": {
"testMatch": [
"**/__tests__/**/?(*.)(spec|test).ts?(x)"
"**/__tests__/**/?(*.)(spec|test).js?(x)"
],
"setupFiles": [
"./__tests__/setupMissingGlobals.ts"
"./__tests__/setupMissingGlobals.js"
],
"coverageDirectory": "./coverage/",
"collectCoverage": true
@@ -193,8 +162,7 @@
"test": {
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-function-bind",
@@ -216,6 +184,12 @@
"loose": true
}
],
[
"@babel/plugin-proposal-private-property-in-object",
{
"loose": true
}
],
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-transform-react-constant-elements",
"@babel/plugin-transform-runtime",
+455
View File
@@ -0,0 +1,455 @@
/*
Action creators for user annotation
*/
import difference from "lodash.difference";
import pako from "pako";
import * as globals from "../globals";
import { MatrixFBS, AnnotationsHelpers } from "../util/stateManager";
const { isUserAnnotation } = AnnotationsHelpers;
export const annotationCreateCategoryAction =
(newCategoryName, categoryToDuplicate) => async (dispatch, getState) => {
/*
Add a new user-created category to the obs annotations.
Arguments:
newCategoryName - string name for the category.
categoryToDuplicate - obs category to use for initial values, or null.
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
const { schema } = prevAnnoMatrix;
/* name must be a string, non-zero length */
if (typeof newCategoryName !== "string" || newCategoryName.length === 0)
throw new Error("user annotations require string name");
/* ensure the name isn't already in use! */
if (schema.annotations.obsByName[newCategoryName])
throw new Error("name collision on annotation category create");
let initialValue;
let newSchema;
let ctor;
if (categoryToDuplicate) {
/* if we are duplicating a category, retrieve it */
const catDupSchema = schema.annotations.obsByName[categoryToDuplicate];
const catDupType = catDupSchema?.type;
if (catDupType !== "string" && catDupType !== "categorical")
throw new Error(
"categoryToDuplicate does not exist or has invalid type"
);
const catToDupDf = await prevAnnoMatrix
.base()
.fetch("obs", categoryToDuplicate);
const col = catToDupDf.col(categoryToDuplicate);
initialValue = col.asArray();
const { categories } = col.summarizeCategorical();
// all user-created annotations must have the unassigned category
if (!categories.includes(globals.unassignedCategoryLabel)) {
categories.push(globals.unassignedCategoryLabel);
}
ctor = initialValue.constructor;
newSchema = {
...catDupSchema,
name: newCategoryName,
categories,
writable: true,
};
} else {
/* else assign to the standard default value */
initialValue = globals.unassignedCategoryLabel;
ctor = Array;
newSchema = {
name: newCategoryName,
type: "categorical",
categories: [globals.unassignedCategoryLabel],
writable: true,
};
}
const obsCrossfilter = prevObsCrossfilter.addObsColumn(
newSchema,
ctor,
initialValue
);
dispatch({
type: "annotation: create category",
data: newCategoryName,
categoryToDuplicate,
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
});
};
export const annotationRenameCategoryAction =
(oldCategoryName, newCategoryName) => (dispatch, getState) => {
/*
Rename a user-created annotation category
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, oldCategoryName))
throw new Error("not a user annotation");
/* name must be a string, non-zero length */
if (typeof newCategoryName !== "string" || newCategoryName.length === 0)
throw new Error("user annotations require string name");
if (oldCategoryName === newCategoryName) return;
const obsCrossfilter = prevObsCrossfilter.renameObsColumn(
oldCategoryName,
newCategoryName
);
dispatch({
type: "annotation: category edited",
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
metadataField: oldCategoryName,
newCategoryText: newCategoryName,
data: newCategoryName,
});
};
export const annotationDeleteCategoryAction =
(categoryName) => (dispatch, getState) => {
/*
Delete a user-created category
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
const obsCrossfilter = prevObsCrossfilter.dropObsColumn(categoryName);
dispatch({
type: "annotation: delete category",
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
metadataField: categoryName,
});
};
export const annotationCreateLabelInCategory =
(categoryName, labelName, assignSelected) => async (dispatch, getState) => {
/*
Add a new label to a user-defined category. If assignSelected is true, assign
the label to all currently selected cells.
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
let obsCrossfilter = prevObsCrossfilter.addObsAnnoCategory(
categoryName,
labelName
);
if (assignSelected) {
obsCrossfilter = await obsCrossfilter.setObsColumnValues(
categoryName,
prevObsCrossfilter.allSelectedLabels(),
labelName
);
}
dispatch({
type: "annotation: add new label to category",
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
metadataField: categoryName,
newLabelText: labelName,
assignSelectedCells: assignSelected,
});
};
export const annotationDeleteLabelFromCategory =
(categoryName, labelName) => async (dispatch, getState) => {
/*
delete a label from a user-defined category
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
const obsCrossfilter = await prevObsCrossfilter.removeObsAnnoCategory(
categoryName,
labelName,
globals.unassignedCategoryLabel
);
dispatch({
type: "annotation: delete label",
metadataField: categoryName,
label: labelName,
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
});
};
export const annotationRenameLabelInCategory =
(categoryName, oldLabelName, newLabelName) => async (dispatch, getState) => {
/*
label name change
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
let obsCrossfilter = await prevObsCrossfilter.resetObsColumnValues(
categoryName,
oldLabelName,
newLabelName
);
obsCrossfilter = await obsCrossfilter.removeObsAnnoCategory(
categoryName,
oldLabelName,
globals.unassignedCategoryLabel
);
dispatch({
type: "annotation: label edited",
editedLabel: newLabelName,
metadataField: categoryName,
label: oldLabelName,
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
});
};
export const annotationLabelCurrentSelection =
(categoryName, labelName) => async (dispatch, getState) => {
/*
set the label on all currently selected
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
const obsCrossfilter = await prevObsCrossfilter.setObsColumnValues(
categoryName,
prevObsCrossfilter.allSelectedLabels(),
labelName
);
dispatch({
type: "annotation: label current cell selection",
metadataField: categoryName,
label: labelName,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
function writableAnnotations(annoMatrix) {
return annoMatrix.schema.annotations.obs.columns
.filter((s) => s.writable)
.map((s) => s.name);
}
export const needToSaveObsAnnotations = (annoMatrix, lastSavedAnnoMatrix) => {
/*
Return true if there are LIKELY user-defined annotation modifications between the two
annoMatrices. Technically not an action creator, but intimately intertwined
with the save process.
Two conditions will trigger a need to save:
* the collection of user-defined columns have changed
* the contents of the user-defined columns have change
*/
annoMatrix = annoMatrix.base();
// if the annoMatrix hasn't changed, we are guaranteed no changes to the matrix schema or contents.
if (annoMatrix === lastSavedAnnoMatrix) return false;
// if the schema has changed, we need to save
const currentWritable = writableAnnotations(annoMatrix);
if (difference(currentWritable, writableAnnotations(lastSavedAnnoMatrix))) {
return true;
}
// no schema changes; check for change in contents
return currentWritable.some(
(col) => annoMatrix.col(col) !== lastSavedAnnoMatrix.col(col)
);
};
export const saveObsAnnotationsAction = () => async (dispatch, getState) => {
/*
Save the user-created obs annotations IF any have changed.
*/
const state = getState();
const { annotations, autosave } = state;
const { dataCollectionNameIsReadOnly, dataCollectionName } = annotations;
const { lastSavedAnnoMatrix, saveInProgress } = autosave;
const annoMatrix = state.annoMatrix.base();
if (saveInProgress || annoMatrix === lastSavedAnnoMatrix) return;
if (!needToSaveObsAnnotations(annoMatrix, lastSavedAnnoMatrix)) {
dispatch({
type: "writable obs annotations - save complete",
lastSavedAnnoMatrix: annoMatrix,
});
return;
}
/*
Else, we really do need to save
*/
dispatch({
type: "writable obs annotations - save started",
});
const df = await annoMatrix.fetch("obs", writableAnnotations(annoMatrix));
const matrix = MatrixFBS.encodeMatrixFBS(df);
const compressedMatrix = pako.deflate(matrix);
try {
const queryString =
!dataCollectionNameIsReadOnly && !!dataCollectionName
? `?annotation-collection-name=${encodeURIComponent(
dataCollectionName
)}`
: "";
const res = await fetch(
`${globals.API.prefix}${globals.API.version}annotations/obs${queryString}`,
{
method: "PUT",
body: compressedMatrix,
headers: new Headers({
"Content-Type": "application/octet-stream",
}),
credentials: "include",
}
);
if (res.ok) {
dispatch({
type: "writable obs annotations - save complete",
lastSavedAnnoMatrix: annoMatrix,
});
} else {
dispatch({
type: "writable obs annotations - save error",
message: `HTTP error ${res.status} - ${res.statusText}`,
res,
});
}
} catch (error) {
dispatch({
type: "writable obs annotations - save error",
message: error.toString(),
error,
});
}
};
export const saveGenesetsAction = () => async (dispatch, getState) => {
const state = getState();
// bail if gene sets not available, or in readonly mode.
const { config } = state;
const { lastTid, genesets } = state.genesets;
const genesetsAreAvailable =
config?.parameters?.annotations_genesets ?? false;
const genesetsReadonly =
config?.parameters?.annotations_genesets_readonly ?? true;
if (!genesetsAreAvailable || genesetsReadonly) {
// our non-save was completed!
return dispatch({
type: "autosave: genesets complete",
lastSavedGenesets: genesets,
});
}
dispatch({
type: "autosave: genesets started",
});
/* Create the JSON OTA data structure */
const tid = (lastTid ?? 0) + 1;
const genesetsOTA = [];
for (const [name, gs] of genesets) {
const genes = [];
for (const g of gs.genes.values()) {
genes.push({
gene_symbol: g.geneSymbol,
gene_description: g.geneDescription,
});
}
genesetsOTA.push({
geneset_name: name,
geneset_description: gs.genesetDescription,
genes,
});
}
const ota = {
tid,
genesets: genesetsOTA,
};
/* Save to server */
try {
const { dataCollectionNameIsReadOnly, dataCollectionName } =
state.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(ota),
credentials: "include",
}
);
if (!res.ok) {
return dispatch({
type: "autosave: genesets error",
message: `HTTP error ${res.status} - ${res.statusText}`,
res,
});
}
return Promise.all([
dispatch({
type: "autosave: genesets complete",
lastSavedGenesets: genesets,
}),
dispatch({
type: "geneset: set tid",
tid,
}),
]);
} catch (error) {
return dispatch({
type: "autosave: genesets error",
message: error.toString(),
error,
});
}
};
-530
View File
@@ -1,530 +0,0 @@
/*
Action creators for user annotation
*/
import difference from "lodash.difference";
import pako from "pako";
import * as globals from "../globals";
import { MatrixFBS, AnnotationsHelpers } from "../util/stateManager";
const { isUserAnnotation } = AnnotationsHelpers;
export const annotationCreateCategoryAction = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
newCategoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryToDuplicate: any
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
/*
Add a new user-created category to the obs annotations.
Arguments:
newCategoryName - string name for the category.
categoryToDuplicate - obs category to use for initial values, or null.
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
const { schema } = prevAnnoMatrix;
/* name must be a string, non-zero length */
if (typeof newCategoryName !== "string" || newCategoryName.length === 0)
throw new Error("user annotations require string name");
/* ensure the name isn't already in use! */
if (schema.annotations.obsByName[newCategoryName])
throw new Error("name collision on annotation category create");
let initialValue;
let newSchema;
let ctor;
if (categoryToDuplicate) {
/* if we are duplicating a category, retrieve it */
const catDupSchema = schema.annotations.obsByName[categoryToDuplicate];
const catDupType = catDupSchema?.type;
if (catDupType !== "string" && catDupType !== "categorical")
throw new Error("categoryToDuplicate does not exist or has invalid type");
const catToDupDf = await prevAnnoMatrix
.base()
.fetch("obs", categoryToDuplicate);
const col = catToDupDf.col(categoryToDuplicate);
initialValue = col.asArray();
const { categories } = col.summarizeCategorical();
// all user-created annotations must have the unassigned category
if (!categories.includes(globals.unassignedCategoryLabel)) {
categories.push(globals.unassignedCategoryLabel);
}
ctor = initialValue.constructor;
newSchema = {
...catDupSchema,
name: newCategoryName,
categories,
writable: true,
};
} else {
/* else assign to the standard default value */
initialValue = globals.unassignedCategoryLabel;
ctor = Array;
newSchema = {
name: newCategoryName,
type: "categorical",
categories: [globals.unassignedCategoryLabel],
writable: true,
};
}
const obsCrossfilter = prevObsCrossfilter.addObsColumn(
newSchema,
ctor,
initialValue
);
dispatch({
type: "annotation: create category",
data: newCategoryName,
categoryToDuplicate,
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
});
};
export const annotationRenameCategoryAction = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
oldCategoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
newCategoryName: any
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => (dispatch: any, getState: any) => {
/*
Rename a user-created annotation category
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, oldCategoryName))
throw new Error("not a user annotation");
/* name must be a string, non-zero length */
if (typeof newCategoryName !== "string" || newCategoryName.length === 0)
throw new Error("user annotations require string name");
if (oldCategoryName === newCategoryName) return;
const obsCrossfilter = prevObsCrossfilter.renameObsColumn(
oldCategoryName,
newCategoryName
);
dispatch({
type: "annotation: category edited",
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
metadataField: oldCategoryName,
newCategoryText: newCategoryName,
data: newCategoryName,
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const annotationDeleteCategoryAction = (categoryName: any) => (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
/*
Delete a user-created category
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
const obsCrossfilter = prevObsCrossfilter.dropObsColumn(categoryName);
dispatch({
type: "annotation: delete category",
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
metadataField: categoryName,
});
};
export const annotationCreateLabelInCategory = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
labelName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
assignSelected: any // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
/*
Add a new label to a user-defined category. If assignSelected is true, assign
the label to all currently selected cells.
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
let obsCrossfilter = prevObsCrossfilter.addObsAnnoCategory(
categoryName,
labelName
);
if (assignSelected) {
obsCrossfilter = await obsCrossfilter.setObsColumnValues(
categoryName,
prevObsCrossfilter.allSelectedLabels(),
labelName
);
}
dispatch({
type: "annotation: add new label to category",
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
metadataField: categoryName,
newLabelText: labelName,
assignSelectedCells: assignSelected,
});
};
export const annotationDeleteLabelFromCategory = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
labelName: any // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
/*
delete a label from a user-defined category
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
const obsCrossfilter = await prevObsCrossfilter.removeObsAnnoCategory(
categoryName,
labelName,
globals.unassignedCategoryLabel
);
dispatch({
type: "annotation: delete label",
metadataField: categoryName,
label: labelName,
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
});
};
export const annotationRenameLabelInCategory = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
oldLabelName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
newLabelName: any
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
/*
label name change
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
let obsCrossfilter = await prevObsCrossfilter.resetObsColumnValues(
categoryName,
oldLabelName,
newLabelName
);
obsCrossfilter = await obsCrossfilter.removeObsAnnoCategory(
categoryName,
oldLabelName,
globals.unassignedCategoryLabel
);
dispatch({
type: "annotation: label edited",
editedLabel: newLabelName,
metadataField: categoryName,
label: oldLabelName,
annoMatrix: obsCrossfilter.annoMatrix,
obsCrossfilter,
});
};
export const annotationLabelCurrentSelection = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
labelName: any
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
/*
set the label on all currently selected
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
if (!prevAnnoMatrix || !prevObsCrossfilter) return;
if (!isUserAnnotation(prevAnnoMatrix, categoryName))
throw new Error("not a user annotation");
const obsCrossfilter = await prevObsCrossfilter.setObsColumnValues(
categoryName,
prevObsCrossfilter.allSelectedLabels(),
labelName
);
dispatch({
type: "annotation: label current cell selection",
metadataField: categoryName,
label: labelName,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function writableAnnotations(annoMatrix: any) {
return (
annoMatrix.schema.annotations.obs.columns
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.filter((s: any) => s.writable)
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.map((s: any) => s.name)
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export const needToSaveObsAnnotations = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
annoMatrix: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
lastSavedAnnoMatrix: any
) => {
/*
Return true if there are LIKELY user-defined annotation modifications between the two
annoMatrices. Technically not an action creator, but intimately intertwined
with the save process.
Two conditions will trigger a need to save:
* the collection of user-defined columns have changed
* the contents of the user-defined columns have change
*/
annoMatrix = annoMatrix.base();
// if the annoMatrix hasn't changed, we are guaranteed no changes to the matrix schema or contents.
if (annoMatrix === lastSavedAnnoMatrix) return false;
// if the schema has changed, we need to save
const currentWritable = writableAnnotations(annoMatrix);
if (difference(currentWritable, writableAnnotations(lastSavedAnnoMatrix))) {
return true;
}
// no schema changes; check for change in contents
return currentWritable.some(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(col: any) => annoMatrix.col(col) !== lastSavedAnnoMatrix.col(col)
);
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export const saveObsAnnotationsAction = () => async (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
/*
Save the user-created obs annotations IF any have changed.
*/
const state = getState();
const { annotations, autosave } = state;
const { dataCollectionNameIsReadOnly, dataCollectionName } = annotations;
const { lastSavedAnnoMatrix, saveInProgress } = autosave;
const annoMatrix = state.annoMatrix.base();
if (saveInProgress || annoMatrix === lastSavedAnnoMatrix) return;
if (!needToSaveObsAnnotations(annoMatrix, lastSavedAnnoMatrix)) {
dispatch({
type: "writable obs annotations - save complete",
lastSavedAnnoMatrix: annoMatrix,
});
return;
}
/*
Else, we really do need to save
*/
dispatch({
type: "writable obs annotations - save started",
});
const df = await annoMatrix.fetch("obs", writableAnnotations(annoMatrix));
const matrix = MatrixFBS.encodeMatrixFBS(df);
const compressedMatrix = pako.deflate(matrix);
try {
const queryString =
!dataCollectionNameIsReadOnly && !!dataCollectionName
? `?annotation-collection-name=${encodeURIComponent(
dataCollectionName
)}`
: "";
const res = await fetch(
`${globals.API.prefix}${globals.API.version}annotations/obs${queryString}`,
{
method: "PUT",
body: compressedMatrix,
headers: new Headers({
"Content-Type": "application/octet-stream",
}),
credentials: "include",
}
);
if (res.ok) {
dispatch({
type: "writable obs annotations - save complete",
lastSavedAnnoMatrix: annoMatrix,
});
} else {
dispatch({
type: "writable obs annotations - save error",
message: `HTTP error ${res.status} - ${res.statusText}`,
res,
});
}
} catch (error) {
dispatch({
type: "writable obs annotations - save error",
message: error.toString(),
error,
});
}
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export const saveGenesetsAction = () => async (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
// bail if gene sets not available, or in readonly mode.
const { config } = state;
const { lastTid, genesets } = state.genesets;
const genesetsAreAvailable =
config?.parameters?.annotations_genesets ?? false;
const genesetsReadonly =
config?.parameters?.annotations_genesets_readonly ?? true;
if (!genesetsAreAvailable || genesetsReadonly) {
// our non-save was completed!
return dispatch({
type: "autosave: genesets complete",
lastSavedGenesets: genesets,
});
}
dispatch({
type: "autosave: genesets started",
});
/* Create the JSON OTA data structure */
const tid = (lastTid ?? 0) + 1;
const genesetsOTA = [];
for (const [name, gs] of genesets) {
const genes = [];
for (const g of gs.genes.values()) {
genes.push({
gene_symbol: g.geneSymbol,
gene_description: g.geneDescription,
});
}
genesetsOTA.push({
geneset_name: name,
geneset_description: gs.genesetDescription,
genes,
});
}
const ota = {
tid,
genesets: genesetsOTA,
};
/* Save to server */
try {
const {
dataCollectionNameIsReadOnly,
dataCollectionName,
} = state.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(ota),
credentials: "include",
}
);
if (!res.ok) {
return dispatch({
type: "autosave: genesets error",
message: `HTTP error ${res.status} - ${res.statusText}`,
res,
});
}
return await Promise.all([
dispatch({
type: "autosave: genesets complete",
lastSavedGenesets: genesets,
}),
dispatch({
type: "geneset: set tid",
tid,
}),
]);
} catch (error) {
return dispatch({
type: "autosave: genesets error",
message: error.toString(),
error,
});
}
};
+47
View File
@@ -0,0 +1,47 @@
/*
action creators related to embeddings choice
*/
import { AnnoMatrixObsCrossfilter } from "../annoMatrix";
import { _setEmbeddingSubset } from "../util/stateManager/viewStackHelpers";
export async function _switchEmbedding(
prevAnnoMatrix,
prevCrossfilter,
newEmbeddingName
) {
/*
DRY helper used by embedding action creators
*/
const base = prevAnnoMatrix.base();
const embeddingDf = await base.fetch("emb", newEmbeddingName);
const annoMatrix = _setEmbeddingSubset(prevAnnoMatrix, embeddingDf);
const obsCrossfilter = await new AnnoMatrixObsCrossfilter(
annoMatrix,
prevCrossfilter.obsCrossfilter
).select("emb", newEmbeddingName, {
mode: "all",
});
return [annoMatrix, obsCrossfilter];
}
export const layoutChoiceAction =
(newLayoutChoice) => async (dispatch, getState) => {
/*
On layout choice, make sure we have selected all on the previous layout, AND the new
layout.
*/
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevCrossfilter } =
getState();
const [annoMatrix, obsCrossfilter] = await _switchEmbedding(
prevAnnoMatrix,
prevCrossfilter,
newLayoutChoice
);
dispatch({
type: "set layout choice",
layoutChoice: newLayoutChoice,
obsCrossfilter,
annoMatrix,
});
};
-58
View File
@@ -1,58 +0,0 @@
/*
action creators related to embeddings choice
*/
import { AnnoMatrixObsCrossfilter } from "../annoMatrix";
import { _setEmbeddingSubset } from "../util/stateManager/viewStackHelpers";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export async function _switchEmbedding(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
prevAnnoMatrix: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
prevCrossfilter: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
newEmbeddingName: any
) {
/*
DRY helper used by embedding action creators
*/
const base = prevAnnoMatrix.base();
const embeddingDf = await base.fetch("emb", newEmbeddingName);
const annoMatrix = _setEmbeddingSubset(prevAnnoMatrix, embeddingDf);
const obsCrossfilter = await new AnnoMatrixObsCrossfilter(
annoMatrix,
prevCrossfilter.obsCrossfilter
).select("emb", newEmbeddingName, {
mode: "all",
});
return [annoMatrix, obsCrossfilter];
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const layoutChoiceAction = (newLayoutChoice: any) => async (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
/*
On layout choice, make sure we have selected all on the previous layout, AND the new
layout.
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevCrossfilter,
} = getState();
const [annoMatrix, obsCrossfilter] = await _switchEmbedding(
prevAnnoMatrix,
prevCrossfilter,
newLayoutChoice
);
dispatch({
type: "set layout choice",
layoutChoice: newLayoutChoice,
obsCrossfilter,
annoMatrix,
});
};
+149
View File
@@ -0,0 +1,149 @@
import { postUserErrorToast } from "../components/framework/toasters";
/*
Action creators for gene sets
Primarily used to keep the crossfilter and underlying data in sync with the UI.
The behavior manifest in these action creators:
Delete a gene set, will
* drop index & clear selection state on the gene set summary
* drop index & clear selection state of each gene in the geneset
Delete a gene from a gene set, will:
* drop index & clear selection state on the gene set summary
* drop index & clear selection state on the gene
Add a gene to a gene set, will:
* drop index & clear selection state on the gene set summary
* will NOT touch the selection state for the gene
Note that crossfilter indices are lazy created, as needed.
*/
export const genesetDelete = (genesetName) => (dispatch, getState) => {
const state = getState();
const { genesets } = state;
const gs = genesets?.genesets?.get(genesetName) ?? {};
const geneSymbols = Array.from(gs.genes.keys());
const obsCrossfilter = dropGeneset(dispatch, state, genesetName, geneSymbols);
if (genesetName === state.colors.colorAccessor) {
dispatch({
type: "reset colorscale",
});
}
dispatch({
type: "geneset: delete",
genesetName,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
export const genesetAddGenes =
(genesetName, genes) => async (dispatch, getState) => {
const state = getState();
const { obsCrossfilter: prevObsCrossfilter, annoMatrix } = state;
const { schema } = annoMatrix;
const varIndex = schema.annotations.var.index;
const df = await annoMatrix.fetch("var", varIndex);
const geneNames = df.col(varIndex).asArray();
genes = genes.reduce((acc, gene) => {
if (geneNames.indexOf(gene.geneSymbol) === -1) {
postUserErrorToast(
`${gene.geneSymbol} doesn't appear to be a valid gene name.`
);
} else acc.push(gene);
return acc;
}, []);
const obsCrossfilter = dropGenesetSummaryDimension(
prevObsCrossfilter,
state,
genesetName
);
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isGeneSetSummary: true },
selection: genesetName,
});
return dispatch({
type: "geneset: add genes",
genesetName,
genes,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
export const genesetDeleteGenes =
(genesetName, geneSymbols) => (dispatch, getState) => {
const state = getState();
const obsCrossfilter = dropGeneset(
dispatch,
state,
genesetName,
geneSymbols
);
return dispatch({
type: "geneset: delete genes",
genesetName,
geneSymbols,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
/*
Private
*/
function dropGenesetSummaryDimension(obsCrossfilter, state, genesetName) {
const { annoMatrix, genesets } = state;
const varIndex = annoMatrix.schema.annotations?.var?.index;
const gs = genesets?.genesets?.get(genesetName) ?? {};
const genes = Array.from(gs.genes.keys());
const query = {
summarize: {
method: "mean",
field: "var",
column: varIndex,
values: genes,
},
};
return obsCrossfilter.dropDimension("X", query);
}
function dropGeneDimension(obsCrossfilter, state, gene) {
const { annoMatrix } = state;
const varIndex = annoMatrix.schema.annotations?.var?.index;
const query = {
where: {
field: "var",
column: varIndex,
value: gene,
},
};
return obsCrossfilter.dropDimension("X", query);
}
function dropGeneset(dispatch, state, genesetName, geneSymbols) {
const { obsCrossfilter: prevObsCrossfilter } = state;
const obsCrossfilter = geneSymbols.reduce(
(crossfilter, gene) => dropGeneDimension(crossfilter, state, gene),
dropGenesetSummaryDimension(prevObsCrossfilter, state, genesetName)
);
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isGeneSetSummary: true },
selection: genesetName,
});
geneSymbols.forEach((g) =>
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isUserDefined: true },
selection: g,
})
);
return obsCrossfilter;
}
-181
View File
@@ -1,181 +0,0 @@
import { postUserErrorToast } from "../components/framework/toasters";
/*
Action creators for gene sets
Primarily used to keep the crossfilter and underlying data in sync with the UI.
The behavior manifest in these action creators:
Delete a gene set, will
* drop index & clear selection state on the gene set summary
* drop index & clear selection state of each gene in the geneset
Delete a gene from a gene set, will:
* drop index & clear selection state on the gene set summary
* drop index & clear selection state on the gene
Add a gene to a gene set, will:
* drop index & clear selection state on the gene set summary
* will NOT touch the selection state for the gene
Note that crossfilter indices are lazy created, as needed.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const genesetDelete = (genesetName: any) => (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
const { genesets } = state;
const gs = genesets?.genesets?.get(genesetName) ?? {};
const geneSymbols = Array.from(gs.genes.keys());
const obsCrossfilter = dropGeneset(dispatch, state, genesetName, geneSymbols);
if (genesetName === state.colors.colorAccessor) {
dispatch({
type: "reset colorscale",
});
}
dispatch({
type: "geneset: delete",
genesetName,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const genesetAddGenes = (genesetName: any, genes: any) => async (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
const { obsCrossfilter: prevObsCrossfilter, annoMatrix } = state;
const { schema } = annoMatrix;
const varIndex = schema.annotations.var.index;
const df = await annoMatrix.fetch("var", varIndex);
const geneNames = df.col(varIndex).asArray();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
genes = genes.reduce((acc: any, gene: any) => {
if (geneNames.indexOf(gene.geneSymbol) === -1) {
postUserErrorToast(
`${gene.geneSymbol} doesn't appear to be a valid gene name.`
);
} else acc.push(gene);
return acc;
}, []);
const obsCrossfilter = dropGenesetSummaryDimension(
prevObsCrossfilter,
state,
genesetName
);
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isGeneSetSummary: true },
selection: genesetName,
});
return dispatch({
type: "geneset: add genes",
genesetName,
genes,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const genesetDeleteGenes = (genesetName: any, geneSymbols: any) => (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
const obsCrossfilter = dropGeneset(dispatch, state, genesetName, geneSymbols);
return dispatch({
type: "geneset: delete genes",
genesetName,
geneSymbols,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
/*
Private
*/
function dropGenesetSummaryDimension(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
obsCrossfilter: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
state: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
genesetName: any
) {
const { annoMatrix, genesets } = state;
const varIndex = annoMatrix.schema.annotations?.var?.index;
const gs = genesets?.genesets?.get(genesetName) ?? {};
const genes = Array.from(gs.genes.keys());
const query = {
summarize: {
method: "mean",
field: "var",
column: varIndex,
values: genes,
},
};
return obsCrossfilter.dropDimension("X", query);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function dropGeneDimension(obsCrossfilter: any, state: any, gene: any) {
const { annoMatrix } = state;
const varIndex = annoMatrix.schema.annotations?.var?.index;
const query = {
where: {
field: "var",
column: varIndex,
value: gene,
},
};
return obsCrossfilter.dropDimension("X", query);
}
function dropGeneset(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
state: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
genesetName: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
geneSymbols: any
) {
const { obsCrossfilter: prevObsCrossfilter } = state;
const obsCrossfilter = geneSymbols.reduce(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(crossfilter: any, gene: any) =>
dropGeneDimension(crossfilter, state, gene),
dropGenesetSummaryDimension(prevObsCrossfilter, state, genesetName)
);
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isGeneSetSummary: true },
selection: genesetName,
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
geneSymbols.forEach((g: any) =>
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isUserDefined: true },
selection: g,
})
);
return obsCrossfilter;
}
@@ -12,8 +12,7 @@ import * as viewActions from "./viewStack";
import * as embActions from "./embedding";
import * as genesetActions from "./geneset";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function setGlobalConfig(config: any) {
function setGlobalConfig(config) {
/**
* Set any global run-time config not _exclusively_ managed by the config reducer.
* This should only set fields defined in globals.globalConfig.
@@ -26,8 +25,7 @@ function setGlobalConfig(config: any) {
/*
return promise fetching user-configured colors
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function userColorsFetchAndLoad(dispatch: any) {
async function userColorsFetchAndLoad(dispatch) {
return fetchJson("colors").then((response) =>
dispatch({
type: "universe: user color load success",
@@ -40,8 +38,7 @@ async function schemaFetch() {
return fetchJson("schema");
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function configFetch(dispatch: any) {
async function configFetch(dispatch) {
return fetchJson("config").then((response) => {
const config = { ...globals.configDefaults, ...response.config };
@@ -55,8 +52,7 @@ async function configFetch(dispatch: any) {
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function userInfoFetch(dispatch: any) {
async function userInfoFetch(dispatch) {
return fetchJson("userinfo").then((response) => {
const { userinfo: userInfo } = response || {};
dispatch({
@@ -67,8 +63,7 @@ async function userInfoFetch(dispatch: any) {
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async function genesetsFetch(dispatch: any, config: any) {
async function genesetsFetch(dispatch, config) {
/* request genesets ONLY if the backend supports the feature */
const defaultResponse = {
genesets: [],
@@ -89,31 +84,25 @@ async function genesetsFetch(dispatch: any, config: any) {
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function prefetchEmbeddings(annoMatrix: any) {
function prefetchEmbeddings(annoMatrix) {
/*
prefetch requests for all embeddings
*/
const { schema } = annoMatrix;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const available = schema.layout.obs.map((v: any) => v.name);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
available.forEach((embName: any) => annoMatrix.prefetch("emb", embName));
const available = schema.layout.obs.map((v) => v.name);
available.forEach((embName) => annoMatrix.prefetch("emb", embName));
}
/*
Application bootstrap
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
const doInitialDataLoad = () =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
catchErrorsWrap(async (dispatch: any) => {
catchErrorsWrap(async (dispatch) => {
dispatch({ type: "initial data load start" });
try {
const [config, schema] = await Promise.all([
configFetch(dispatch),
// @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 1.
schemaFetch(dispatch),
userColorsFetchAndLoad(dispatch),
userInfoFetch(dispatch),
@@ -137,8 +126,7 @@ const doInitialDataLoad = () =>
const layoutSchema = schema?.schema?.layout?.obs ?? [];
if (
defaultEmbedding &&
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutSchema.some((s: any) => s.name === defaultEmbedding)
layoutSchema.some((s) => s.name === defaultEmbedding)
) {
dispatch(embActions.layoutChoiceAction(defaultEmbedding));
}
@@ -147,25 +135,21 @@ const doInitialDataLoad = () =>
}
}, true);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
function requestSingleGeneExpressionCountsForColoringPOST(gene: any) {
function requestSingleGeneExpressionCountsForColoringPOST(gene) {
return {
type: "color by expression",
gene,
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
const requestUserDefinedGene = (gene: any) => ({
const requestUserDefinedGene = (gene) => ({
type: "request user defined gene success",
data: {
genes: [gene],
},
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const dispatchDiffExpErrors = (dispatch: any, response: any) => {
const dispatchDiffExpErrors = (dispatch, response) => {
switch (response.status) {
case 403:
dispatchNetworkErrorMessageToUser(
@@ -188,84 +172,76 @@ const dispatchDiffExpErrors = (dispatch: any, response: any) => {
}
};
const requestDifferentialExpression = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
set1: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
set2: any,
num_genes = 50
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
dispatch({ type: "request differential expression started" });
try {
/*
const requestDifferentialExpression =
(set1, set2, num_genes = 50) =>
async (dispatch, getState) => {
dispatch({ type: "request differential expression started" });
try {
/*
Steps:
1. get the most differentially expressed genes
2. get expression data for each
*/
const { annoMatrix } = getState();
const varIndexName = annoMatrix.schema.annotations.var.index;
const { annoMatrix } = getState();
const varIndexName = annoMatrix.schema.annotations.var.index;
// Legal values are null, Array or TypedArray. Null is initial state.
if (!set1) set1 = [];
if (!set2) set2 = [];
// Legal values are null, Array or TypedArray. Null is initial state.
if (!set1) set1 = [];
if (!set2) set2 = [];
// These lines ensure that we convert any TypedArray to an Array.
// This is necessary because JSON.stringify() does some very strange
// things with TypedArrays (they are marshalled to JSON objects, rather
// than being marshalled as a JSON array).
set1 = Array.isArray(set1) ? set1 : Array.from(set1);
set2 = Array.isArray(set2) ? set2 : Array.from(set2);
// These lines ensure that we convert any TypedArray to an Array.
// This is necessary because JSON.stringify() does some very strange
// things with TypedArrays (they are marshalled to JSON objects, rather
// than being marshalled as a JSON array).
set1 = Array.isArray(set1) ? set1 : Array.from(set1);
set2 = Array.isArray(set2) ? set2 : Array.from(set2);
const res = await fetch(
`${globals.API.prefix}${globals.API.version}diffexp/obs`,
{
method: "POST",
headers: new Headers({
Accept: "application/json",
"Content-Type": "application/json",
}),
body: JSON.stringify({
mode: "topN",
count: num_genes,
set1: { filter: { obs: { index: set1 } } },
set2: { filter: { obs: { index: set2 } } },
}),
credentials: "include",
const res = await fetch(
`${globals.API.prefix}${globals.API.version}diffexp/obs`,
{
method: "POST",
headers: new Headers({
Accept: "application/json",
"Content-Type": "application/json",
}),
body: JSON.stringify({
mode: "topN",
count: num_genes,
set1: { filter: { obs: { index: set1 } } },
set2: { filter: { obs: { index: set2 } } },
}),
credentials: "include",
}
);
if (!res.ok || res.headers.get("Content-Type") !== "application/json") {
return dispatchDiffExpErrors(dispatch, res);
}
);
if (!res.ok || res.headers.get("Content-Type") !== "application/json") {
return dispatchDiffExpErrors(dispatch, res);
const response = await res.json();
const varIndex = await annoMatrix.fetch("var", varIndexName);
const diffexpLists = { negative: [], positive: [] };
for (const polarity of Object.keys(diffexpLists)) {
diffexpLists[polarity] = response[polarity].map((v) => [
varIndex.at(v[0], varIndexName),
...v.slice(1),
]);
}
/* then send the success case action through */
return dispatch({
type: "request differential expression success",
data: diffexpLists,
});
} catch (error) {
return dispatch({
type: "request differential expression error",
error,
});
}
};
const response = await res.json();
const varIndex = await annoMatrix.fetch("var", varIndexName);
const diffexpLists = { negative: [], positive: [] };
for (const polarity of Object.keys(diffexpLists)) {
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
diffexpLists[polarity] = response[polarity].map((v: any) => [
varIndex.at(v[0], varIndexName),
...v.slice(1),
]);
}
/* then send the success case action through */
return dispatch({
type: "request differential expression success",
data: diffexpLists,
});
} catch (error) {
return dispatch({
type: "request differential expression error",
error,
});
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function fetchJson(pathAndQuery: any) {
function fetchJson(pathAndQuery) {
return doJsonRequest(
`${globals.API.prefix}${globals.API.version}${pathAndQuery}`
);
+192
View File
@@ -0,0 +1,192 @@
/*
Action creators for selection
*/
export const selectContinuousMetadataAction =
(type, query, range, oldProps = {}) =>
async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = range
? {
mode: "range",
lo: range[0],
hi: range[1],
inclusive: true, // [lo, hi] incluisve selection
}
: { mode: "all" };
const obsCrossfilter = await prevObsCrossfilter.select(...query, selection);
dispatch({
type,
obsCrossfilter,
range,
...oldProps,
});
};
export const selectCategoricalMetadataAction =
(
type, // action type
metadataField, // annotation category name
labels,
label, // the label being selected/deselected
isSelected, // bool
oldProps = {}
) =>
async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter, categoricalSelection } =
getState();
const labelSelectionState = new Map(categoricalSelection[metadataField]);
labels.forEach(
(l) => labelSelectionState.has(l) || labelSelectionState.set(l, true)
);
labelSelectionState.set(label, isSelected);
const values = Array.from(labelSelectionState.keys()).filter((k) =>
labelSelectionState.get(k)
);
const selection = {
mode: "exact",
values,
};
const obsCrossfilter = await prevObsCrossfilter.select(
"obs",
metadataField,
selection
);
dispatch({
type,
obsCrossfilter,
metadataField,
labelSelectionState,
...oldProps,
});
};
export const selectCategoricalAllMetadataAction =
(
type, // action type
metadataField, // annotation category name
labels,
isSelected, // bool, select all or none
oldProps = {}
) =>
async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter, categoricalSelection } =
getState();
const labelSelectionState = new Map(categoricalSelection[metadataField]);
labels.forEach((label) => labelSelectionState.set(label, isSelected));
const selection = { mode: isSelected ? "all" : "none" };
const obsCrossfilter = await prevObsCrossfilter.select(
"obs",
metadataField,
selection
);
dispatch({
type,
obsCrossfilter,
metadataField,
labelSelectionState,
...oldProps,
});
};
/**
** Graph selection-related actions
**/
export const graphBrushStartAction = () =>
/* no change to crossfilter until a change fires */
({ type: "graph brush start" });
const _graphBrushWithinRectAction =
(type, embName, brushCoords) => async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = { mode: "within-rect", ...brushCoords };
const obsCrossfilter = await prevObsCrossfilter.select(
"emb",
embName,
selection
);
dispatch({
type,
obsCrossfilter,
brushCoords,
});
};
const _graphAllAction = (type, embName) => async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const obsCrossfilter = await prevObsCrossfilter.select("emb", embName, {
mode: "all",
});
dispatch({
type,
obsCrossfilter,
});
};
export const graphBrushChangeAction = (embName, brushCoords) =>
_graphBrushWithinRectAction("graph brush change", embName, brushCoords);
export const graphBrushEndAction = (embName, brushCoords) =>
_graphBrushWithinRectAction("graph brush end", embName, brushCoords);
export const graphBrushCancelAction = (embName) =>
_graphAllAction("graph brush cancel", embName);
export const graphBrushDeselectAction = (embName) =>
_graphAllAction("graph brush deselect", embName);
export const graphLassoStartAction = () =>
/* no change to crossfilter until a change fires */
({ type: "graph lasso start" });
export const graphLassoCancelAction = (embName) =>
_graphAllAction("graph lasso cancel", embName);
export const graphLassoDeselectAction = (embName) =>
_graphAllAction("graph lasso cancel", embName);
export const graphLassoEndAction =
(embName, polygon) => async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = {
mode: "within-polygon",
polygon,
};
const obsCrossfilter = await prevObsCrossfilter.select(
"emb",
embName,
selection
);
dispatch({
type: "graph lasso end",
obsCrossfilter,
polygon,
});
};
/*
Differential expression set selection
*/
export const setCellSetFromSelection = (cellSetId) => (dispatch, getState) => {
const { obsCrossfilter } = getState();
const selected = obsCrossfilter.allSelectedLabels();
dispatch({
type: `store current cell selection as differential set ${cellSetId}`,
data: selected.length > 0 ? selected : null,
});
};
-248
View File
@@ -1,248 +0,0 @@
/*
Action creators for selection
*/
export const selectContinuousMetadataAction = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
type: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
query: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
range: any,
oldProps = {} // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = range
? {
mode: "range",
lo: range[0],
hi: range[1],
inclusive: true, // [lo, hi] incluisve selection
}
: { mode: "all" };
const obsCrossfilter = await prevObsCrossfilter.select(...query, selection);
dispatch({
type,
obsCrossfilter,
range,
...oldProps,
});
};
export const selectCategoricalMetadataAction = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
type: any, // action type
// annotation category name
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
metadataField: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
labels: any,
// the label being selected/deselected
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
label: any,
// bool
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isSelected: any,
oldProps = {}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
const {
obsCrossfilter: prevObsCrossfilter,
categoricalSelection,
} = getState();
const labelSelectionState = new Map(categoricalSelection[metadataField]);
labels.forEach(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(l: any) => labelSelectionState.has(l) || labelSelectionState.set(l, true)
);
labelSelectionState.set(label, isSelected);
const values = Array.from(labelSelectionState.keys()).filter((k) =>
labelSelectionState.get(k)
);
const selection = {
mode: "exact",
values,
};
const obsCrossfilter = await prevObsCrossfilter.select(
"obs",
metadataField,
selection
);
dispatch({
type,
obsCrossfilter,
metadataField,
labelSelectionState,
...oldProps,
});
};
export const selectCategoricalAllMetadataAction = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
type: any, // action type
// annotation category name
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
metadataField: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
labels: any,
// bool, select all or none
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isSelected: any,
oldProps = {}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
const {
obsCrossfilter: prevObsCrossfilter,
categoricalSelection,
} = getState();
const labelSelectionState = new Map(categoricalSelection[metadataField]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
labels.forEach((label: any) => labelSelectionState.set(label, isSelected));
const selection = { mode: isSelected ? "all" : "none" };
const obsCrossfilter = await prevObsCrossfilter.select(
"obs",
metadataField,
selection
);
dispatch({
type,
obsCrossfilter,
metadataField,
labelSelectionState,
...oldProps,
});
};
/**
** Graph selection-related actions
**/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export const graphBrushStartAction = () =>
/* no change to crossfilter until a change fires */
({ type: "graph brush start" });
const _graphBrushWithinRectAction = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
type: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
embName: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
brushCoords: any
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
) => async (dispatch: any, getState: any) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = { mode: "within-rect", ...brushCoords };
const obsCrossfilter = await prevObsCrossfilter.select(
"emb",
embName,
selection
);
dispatch({
type,
obsCrossfilter,
brushCoords,
});
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const _graphAllAction = (type: any, embName: any) => async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const obsCrossfilter = await prevObsCrossfilter.select("emb", embName, {
mode: "all",
});
dispatch({
type,
obsCrossfilter,
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphBrushChangeAction = (embName: any, brushCoords: any) =>
_graphBrushWithinRectAction("graph brush change", embName, brushCoords);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphBrushEndAction = (embName: any, brushCoords: any) =>
_graphBrushWithinRectAction("graph brush end", embName, brushCoords);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphBrushCancelAction = (embName: any) =>
_graphAllAction("graph brush cancel", embName);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphBrushDeselectAction = (embName: any) =>
_graphAllAction("graph brush deselect", embName);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export const graphLassoStartAction = () =>
/* no change to crossfilter until a change fires */
({ type: "graph lasso start" });
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphLassoCancelAction = (embName: any) =>
_graphAllAction("graph lasso cancel", embName);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphLassoDeselectAction = (embName: any) =>
_graphAllAction("graph lasso cancel", embName);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const graphLassoEndAction = (embName: any, polygon: any) => async (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = {
mode: "within-polygon",
polygon,
};
const obsCrossfilter = await prevObsCrossfilter.select(
"emb",
embName,
selection
);
dispatch({
type: "graph lasso end",
obsCrossfilter,
polygon,
});
};
/*
Differential expression set selection
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const setCellSetFromSelection = (cellSetId: any) => (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const { obsCrossfilter } = getState();
const selected = obsCrossfilter.allSelectedLabels();
dispatch({
type: `store current cell selection as differential set ${cellSetId}`,
data: selected.length > 0 ? selected : null,
});
};
@@ -18,13 +18,7 @@ import {
_userResetSubsetAnnoMatrix,
} from "../util/stateManager/viewStackHelpers";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const clipAction = (min: any, max: any) => (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
export const clipAction = (min, max) => (dispatch, getState) => {
/*
apply a clip to the current annoMatrix. By convention, the clip
view is ALWAYS the top view.
@@ -40,8 +34,7 @@ export const clipAction = (min: any, max: any) => (
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const subsetAction = () => (dispatch: any, getState: any) => {
export const subsetAction = () => (dispatch, getState) => {
/*
Subset the annoMatrix to the current crossfilter selection by pushing a
subset view.
@@ -49,10 +42,8 @@ export const subsetAction = () => (dispatch: any, getState: any) => {
By convention, a clip view is ALWAYS the top view, so if present, pop
off and re-apply
*/
const {
annoMatrix: prevAnnoMatrix,
obsCrossfilter: prevObsCrossfilter,
} = getState();
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevObsCrossfilter } =
getState();
const annoMatrix = _userSubsetAnnoMatrix(
prevAnnoMatrix,
prevObsCrossfilter.allSelectedMask()
@@ -65,8 +56,7 @@ export const subsetAction = () => (dispatch: any, getState: any) => {
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const resetSubsetAction = () => (dispatch: any, getState: any) => {
export const resetSubsetAction = () => (dispatch, getState) => {
/*
Reset the annoMatrix to all data. Because we may have multiple views
stacked, we pop them all. By convention, any clip transformation will
@@ -17,39 +17,6 @@ import { _queryValidate, _queryCacheKey } from "./query";
const _dataframeCache = dataframeMemo(128);
export default class AnnoMatrix {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public isView: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public nObs: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public nVar: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public rowIndex: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public schema: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public userFlags: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
public viewOf: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
protected _cache: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
private _pendingLoad: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
private _whereCache: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
private _gcInfo: any;
/*
Abstract base class for all AnnoMatrix objects. This class provides a proxy
to the annotated matrix data authoritatively served by the server/back-end.
@@ -80,7 +47,6 @@ export default class AnnoMatrix {
subset(annoMatrix, rowLabels) -> annoMatrix
etc.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
static fields() {
/*
return the fields present in the AnnoMatrix instance.
@@ -88,8 +54,7 @@ export default class AnnoMatrix {
return ["obs", "var", "emb", "X"];
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
constructor(schema: any, nObs: any, nVar: any, rowIndex = null) {
constructor(schema, nObs, nVar, rowIndex = null) {
/*
Private constructor - this is an abstract base class. Do not use.
*/
@@ -118,13 +83,13 @@ export default class AnnoMatrix {
this.userFlags = {};
/*
Private instance variables.
Private instance variables.
These are caches - lazily loaded. The only guarantee is that if they
are loaded, they will conform to the schema & dimensionality constraints.
These are caches - lazily loaded. The only guarantee is that if they
are loaded, they will conform to the schema & dimensionality constraints.
Do NOT use directly - instead, use the fetch() and preload() API.
*/
Do NOT use directly - instead, use the fetch() and preload() API.
*/
this._cache = {
obs: Dataframe.empty(this.rowIndex),
var: Dataframe.empty(this.rowIndex),
@@ -144,8 +109,6 @@ export default class AnnoMatrix {
/**
** Schema helper/accessors
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
getMatrixColumns(field) {
/*
Return array of column names in the field. ONLY supported on the
@@ -158,7 +121,7 @@ export default class AnnoMatrix {
return _schemaColumns(this.schema, field);
}
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/explicit-module-boundary-types -- need to be able to call this on instances
// eslint-disable-next-line class-methods-use-this -- need to be able to call this on instances
getMatrixFields() {
/*
Return array of fields in this annoMatrix. Currently hard-wired to
@@ -169,8 +132,6 @@ export default class AnnoMatrix {
return AnnoMatrix.fields();
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
getColumnSchema(field, col) {
/*
Return the schema for the field & column ,eg,
@@ -183,8 +144,6 @@ export default class AnnoMatrix {
return _getColumnSchema(this.schema, field, col);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
getColumnDimensions(field, col) {
/*
Return the dimensions on this field / column. For most fields, which are 1D,
@@ -203,12 +162,10 @@ export default class AnnoMatrix {
/**
** General utility methods
**/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
base() {
/*
return the base of view, or `this` if not a view.
*/
// eslint-disable-next-line @typescript-eslint/no-this-alias --- FIXME: disabled temporarily on migrate to TS.
let annoMatrix = this;
while (annoMatrix.isView) annoMatrix = annoMatrix.viewOf;
return annoMatrix;
@@ -217,8 +174,6 @@ export default class AnnoMatrix {
/**
** Load / read interfaces
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
fetch(field, q) {
/*
Return the given query on a single matrix field as a single dataframe.
@@ -276,8 +231,6 @@ export default class AnnoMatrix {
return this._fetch(field, q);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
prefetch(field, q) {
/*
Start a data fetch & cache fill. Identical to fetch() except it does
@@ -308,8 +261,7 @@ export default class AnnoMatrix {
** The actual implementation is in the sub-classes, which MUST override these.
**/
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
addObsAnnoCategory(col, category) {
/*
Add a new category value (aka "label") to a writable obs column, and return the new AnnoMatrix.
@@ -326,8 +278,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
async removeObsAnnoCategory(col, category, unassignedCategory) {
/*
Remove a category value from an obs column, reassign any obs having that value
@@ -348,8 +299,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
dropObsColumn(col) {
/*
Drop an entire writable column, eg a user-created obs annotation. Typical use
@@ -365,8 +315,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'colSchema' is declared but its value is never rea... Remove this comment to see the full error message
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
addObsColumn(colSchema, Ctor, value) {
/*
Add a new writable OBS annotation column, with the caller-specified schema, initial value
@@ -395,8 +344,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'oldCol' is declared but its value is never read.
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
renameObsColumn(oldCol, newCol) {
/*
Rename the obs column 'oldCol' to have name 'newCol' and returns new AnnoMatrix.
@@ -411,8 +359,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
async setObsColumnValues(col, obsLabels, value) {
/*
Set all obs with label in array 'obsLabels' to have 'value'. Typical use would be
@@ -430,8 +377,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
async resetObsColumnValues(col, oldValue, newValue) {
/*
Set by value - all elements in the column with value 'oldValue' are set to 'newValue'.
@@ -448,8 +394,7 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(6133) FIXME: 'colSchema' is declared but its value is never rea... Remove this comment to see the full error message
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this, no-unused-vars -- make sure subclass implements
addEmbedding(colSchema) {
/*
Add a new obs embedding to the AnnoMatrix, with provided schema.
@@ -462,38 +407,28 @@ export default class AnnoMatrix {
_subclassResponsibility();
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
getCacheKeys(field, query) {
/*
Return cache keys for columns associated with this query. May return
[unknown] if no keys are known (ie, nothing is or was cached).
*/
Return cache keys for columns associated with this query. May return
[unknown] if no keys are known (ie, nothing is or was cached).
*/
return _whereCacheGet(this._whereCache, this.schema, field, query);
}
/**
** Private interfaces below.
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_resolveCachedQueries(field, queries) {
return (
queries
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'query' implicitly has an 'any' type.
.map((query) =>
_whereCacheGet(this._whereCache, this.schema, field, query).filter(
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'cacheKey' implicitly has an 'any' type.
(cacheKey) =>
cacheKey !== undefined && this._cache[field].hasCol(cacheKey)
)
return queries
.map((query) =>
_whereCacheGet(this._whereCache, this.schema, field, query).filter(
(cacheKey) =>
cacheKey !== undefined && this._cache[field].hasCol(cacheKey)
)
.flat()
);
)
.flat();
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async _fetch(field, q) {
if (!AnnoMatrix.fields().includes(field)) return undefined;
const queries = Array.isArray(q) ? q : [q];
@@ -506,7 +441,6 @@ Return cache keys for columns associated with this query. May return
/* find any query not already cached */
const uncachedQueries = queries.filter((query) =>
_whereCacheGet(this._whereCache, this.schema, field, query).some(
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'cacheKey' implicitly has an 'any' type.
(cacheKey) =>
cacheKey === undefined || !this._cache[field].hasCol(cacheKey)
)
@@ -516,10 +450,8 @@ Return cache keys for columns associated with this query. May return
if (uncachedQueries.length > 0) {
await Promise.all(
uncachedQueries.map((query) =>
// @ts-expect-error ts-migrate(7006) FIXME: Parameter '_field' implicitly has an 'any' type.
this._getPendingLoad(field, query, async (_field, _query) => {
/* fetch, then index. _doLoad is subclass interface */
// @ts-expect-error ts-migrate(2488) FIXME: Type 'void' must have a '[Symbol.iterator]()' meth... Remove this comment to see the full error message
const [whereCacheUpdate, df] = await this._doLoad(_field, _query);
this._cache[_field] = this._cache[_field].withColsFrom(df);
this._whereCache = _whereCacheMerge(
@@ -540,8 +472,6 @@ Return cache keys for columns associated with this query. May return
return response;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async _getPendingLoad(field, query, fetchFn) {
/*
Given a query on a field, ensure that we only have a single outstanding
@@ -563,7 +493,7 @@ Return cache keys for columns associated with this query. May return
return this._pendingLoad[field][key];
}
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
// eslint-disable-next-line class-methods-use-this -- make sure subclass implements
async _doLoad() {
_subclassResponsibility();
}
@@ -597,22 +527,19 @@ Return cache keys for columns associated with this query. May return
To be effective, the GC callback needs to be invoked from the undo/redo code,
as much of the cache is pinned by that data structure.
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_gcField(field, isHot, pinnedColumns) {
const maxColumns = isHot ? 256 : 10;
const maxColumns = isHot ? 256 : 10; // maybe to aggressive?
const cache = this._cache[field];
if (cache.colIndex.size() < maxColumns) return; // trivial rejection
const candidates = cache.colIndex
.labels()
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
.filter((col) => !pinnedColumns.includes(col));
const excessCount = candidates.length + pinnedColumns.length - maxColumns;
if (excessCount > 0) {
const { _gcInfo } = this;
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'a' implicitly has an 'any' type.
candidates.sort((a, b) => {
let atime = _gcInfo.get(_columnCacheKey(field, a));
if (atime === undefined) atime = 0;
@@ -631,17 +558,13 @@ Return cache keys for columns associated with this query. May return
// )}]`
// );
this._cache[field] = toDrop.reduce(
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'df' implicitly has an 'any' type.
(df, col) => df.dropCol(col),
this._cache[field]
);
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
toDrop.forEach((col) => _gcInfo.delete(_columnCacheKey(field, col)));
}
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_gcFetchCleanup(field, pinnedColumns) {
/*
Called during data load/fetch. By definition, this is 'hot', so we
@@ -656,8 +579,6 @@ Return cache keys for columns associated with this query. May return
}
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'hints' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_gc(hints) {
/*
Called from middleware, or elsewhere. isHot is true if we are in the active store,
@@ -670,8 +591,6 @@ Return cache keys for columns associated with this query. May return
);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_gcUpdateStats(field, dataframe) {
/*
called each time a query is performed, allowing the gc to update any bookkeeping
@@ -681,7 +600,6 @@ Return cache keys for columns associated with this query. May return
const cols = dataframe.colIndex.labels();
const { _gcInfo } = this;
const now = Date.now();
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'c' implicitly has an 'any' type.
cols.forEach((c) => {
_gcInfo.set(_columnCacheKey(field, c), now);
});
@@ -699,8 +617,6 @@ Return cache keys for columns associated with this query. May return
Do not override _clone();
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'clone' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_cloneDeeper(clone) {
clone._cache = _shallowClone(this._cache);
clone._gcInfo = new Map();
@@ -713,7 +629,6 @@ Return cache keys for columns associated with this query. May return
return clone;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_clone() {
const clone = _shallowClone(this);
this._cloneDeeper(clone);
@@ -725,7 +640,6 @@ Return cache keys for columns associated with this query. May return
/*
private utility functions below
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
function _columnCacheKey(field, column) {
return `${field}/${column}`;
}
+6
View File
@@ -0,0 +1,6 @@
/*
Shallow clone an object, correctly handling prototype
*/
export default function _shallowClone(orig) {
return Object.assign(Object.create(Object.getPrototypeOf(orig)), orig);
}
-7
View File
@@ -1,7 +0,0 @@
/*
Shallow clone an object, correctly handling prototype
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export default function _shallowClone(orig: any) {
return Object.assign(Object.create(Object.getPrototypeOf(orig)), orig);
}
+284
View File
@@ -0,0 +1,284 @@
/*
Row crossfilter proxy for an AnnoMatrix. This wraps Crossfilter,
providing a number of services, and ensuring that the crossfilter and
AnnoMatrix stay in sync:
- on-demand index creation as data is loaded
- transparently mapping between queries and crossfilter index names.
- for mutation of the matrix by user annotations, maintain synchronization
between Crossfilter and AnnoMatrix.
*/
import Crossfilter from "../util/typedCrossfilter";
import { _getColumnSchema } from "./schema";
function _dimensionNameFromDf(field, df) {
const colNames = df.colIndex.labels();
return _dimensionName(field, colNames);
}
function _dimensionName(field, colNames) {
if (!Array.isArray(colNames)) return `${field}/${colNames}`;
return `${field}/${colNames.join(":")}`;
}
export default class AnnoMatrixObsCrossfilter {
constructor(annoMatrix, _obsCrossfilter = null) {
this.annoMatrix = annoMatrix;
this.obsCrossfilter =
_obsCrossfilter || new Crossfilter(annoMatrix._cache.obs);
this.obsCrossfilter = this.obsCrossfilter.setData(annoMatrix._cache.obs);
}
size() {
return this.obsCrossfilter.size();
}
/**
Managing the associated annoMatrix. These wrappers are necessary to
make coordinated changes to BOTH the crossfilter and annoMatrix, and
ensure that all state stays synchronized.
See API documentation in annoMatrix.js.
**/
addObsColumn(colSchema, Ctor, value) {
const annoMatrix = this.annoMatrix.addObsColumn(colSchema, Ctor, value);
const obsCrossfilter = this.obsCrossfilter.setData(annoMatrix._cache.obs);
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
dropObsColumn(col) {
const annoMatrix = this.annoMatrix.dropObsColumn(col);
let { obsCrossfilter } = this;
const dimName = _dimensionName("obs", col);
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
renameObsColumn(oldCol, newCol) {
const annoMatrix = this.annoMatrix.renameObsColumn(oldCol, newCol);
const oldDimName = _dimensionName("obs", oldCol);
const newDimName = _dimensionName("obs", newCol);
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(oldDimName)) {
obsCrossfilter = obsCrossfilter.renameDimension(oldDimName, newDimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
addObsAnnoCategory(col, category) {
const annoMatrix = this.annoMatrix.addObsAnnoCategory(col, category);
const dimName = _dimensionName("obs", col);
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
async removeObsAnnoCategory(col, category, unassignedCategory) {
const annoMatrix = await this.annoMatrix.removeObsAnnoCategory(
col,
category,
unassignedCategory
);
const dimName = _dimensionName("obs", col);
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
async setObsColumnValues(col, rowLabels, value) {
const annoMatrix = await this.annoMatrix.setObsColumnValues(
col,
rowLabels,
value
);
const dimName = _dimensionName("obs", col);
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
async resetObsColumnValues(col, oldValue, newValue) {
const annoMatrix = await this.annoMatrix.resetObsColumnValues(
col,
oldValue,
newValue
);
const dimName = _dimensionName("obs", col);
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
addEmbedding(colSchema) {
const annoMatrix = this.annoMatrix.addEmbedding(colSchema);
return new AnnoMatrixObsCrossfilter(annoMatrix, this.obsCrossfilter);
}
/**
* Drop the crossfilter dimension. Do not change the annoMatrix. Useful when we
* want to stop trackin the selection state, but aren't sure we want to blow the
* annomatrix cache.
*/
dropDimension(field, query) {
const { annoMatrix } = this;
let { obsCrossfilter } = this;
const keys = annoMatrix
.getCacheKeys(field, query)
.filter((k) => k !== undefined);
const dimName = _dimensionName(field, keys);
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
/**
Selection state - API is identical to ImmutableTypedCrossfilter, as these
are just wrappers to lazy create indices.
**/
async select(field, query, spec) {
const { annoMatrix } = this;
let { obsCrossfilter } = this;
if (!annoMatrix?._cache?.[field]) {
throw new Error("Unknown field name");
}
if (field === "var") {
throw new Error("unable to obsSelect upon the var dimension");
}
// grab the data, so we can grab the index.
const df = await annoMatrix.fetch(field, query);
const dimName = _dimensionNameFromDf(field, df);
if (!obsCrossfilter.hasDimension(dimName)) {
// lazy index generation - add dimension when first used
obsCrossfilter = this._addObsCrossfilterDimension(
annoMatrix,
obsCrossfilter,
field,
df
);
}
// select
obsCrossfilter = obsCrossfilter.select(dimName, spec);
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
selectAll() {
/*
Select all on any dimension in this field.
*/
const { annoMatrix } = this;
const currentDims = this.obsCrossfilter.dimensionNames();
const obsCrossfilter = currentDims.reduce((xfltr, dim) => xfltr.select(dim, { mode: "all" }), this.obsCrossfilter);
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
countSelected() {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (this.obsCrossfilter.size() === 0) return this.annoMatrix.nObs;
return this.obsCrossfilter.countSelected();
}
allSelectedMask() {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (
this.obsCrossfilter.size() === 0 ||
this.obsCrossfilter.dimensionNames().length === 0
) {
/* fake the mask */
return new Uint8Array(this.annoMatrix.nObs).fill(1);
}
return this.obsCrossfilter.allSelectedMask();
}
allSelectedLabels() {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (
this.obsCrossfilter.size() === 0 ||
this.obsCrossfilter.dimensionNames().length === 0
) {
return this.annoMatrix.rowIndex.labels();
}
const mask = this.obsCrossfilter.allSelectedMask();
const index = this.annoMatrix.rowIndex.isubsetMask(mask);
return index.labels();
}
fillByIsSelected(array, selectedValue, deselectedValue) {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (
this.obsCrossfilter.size() === 0 ||
this.obsCrossfilter.dimensionNames().length === 0
) {
return array.fill(selectedValue);
}
return this.obsCrossfilter.fillByIsSelected(
array,
selectedValue,
deselectedValue
);
}
/**
** Private below
**/
_addObsCrossfilterDimension(annoMatrix, obsCrossfilter, field, df) {
if (field === "var") return obsCrossfilter;
const dimName = _dimensionNameFromDf(field, df);
const dimParams = this._getObsDimensionParams(field, df);
obsCrossfilter = obsCrossfilter.setData(annoMatrix._cache.obs);
obsCrossfilter = obsCrossfilter.addDimension(dimName, ...dimParams);
return obsCrossfilter;
}
_getColumnBaseType(field, col) {
/* Look up the primitive type for this field/col */
const colSchema = _getColumnSchema(this.annoMatrix.schema, field, col);
return colSchema.type;
}
_getObsDimensionParams(field, df) {
/* return the crossfilter dimensiontype type and params for this field/dataframe */
if (field === "emb") {
/* assumed to be 2D */
return ["spatial", df.icol(0).asArray(), df.icol(1).asArray()];
}
/* assumed to be 1D */
const col = df.icol(0);
const colName = df.colIndex.getLabel(0);
const type = this._getColumnBaseType(field, colName);
if (type === "string" || type === "categorical" || type === "boolean") {
return ["enum", col.asArray()];
}
if (type === "int32") {
return ["scalar", col.asArray(), Int32Array];
}
if (type === "float32") {
return ["scalar", col.asArray(), Float32Array];
}
// Currently not supporting boolean and categorical types.
console.error(
`Warning - unknown metadata schema (${type}) for field ${field} ${colName}.`
);
// skip it - we don't know what to do with this type
return undefined;
}
}
-389
View File
@@ -1,389 +0,0 @@
/*
Row crossfilter proxy for an AnnoMatrix. This wraps Crossfilter,
providing a number of services, and ensuring that the crossfilter and
AnnoMatrix stay in sync:
- on-demand index creation as data is loaded
- transparently mapping between queries and crossfilter index names.
- for mutation of the matrix by user annotations, maintain synchronization
between Crossfilter and AnnoMatrix.
*/
import Crossfilter from "../util/typedCrossfilter";
import { _getColumnSchema } from "./schema";
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
function _dimensionNameFromDf(field, df) {
const colNames = df.colIndex.labels();
return _dimensionName(field, colNames);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
function _dimensionName(field, colNames) {
if (!Array.isArray(colNames)) return `${field}/${colNames}`;
return `${field}/${colNames.join(":")}`;
}
export default class AnnoMatrixObsCrossfilter {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
constructor(annoMatrix, _obsCrossfilter = null) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).annoMatrix = annoMatrix;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter =
_obsCrossfilter || new Crossfilter(annoMatrix._cache.obs);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter = (this as any).obsCrossfilter.setData(
annoMatrix._cache.obs
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
size() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).obsCrossfilter.size();
}
/**
Managing the associated annoMatrix. These wrappers are necessary to
make coordinated changes to BOTH the crossfilter and annoMatrix, and
ensure that all state stays synchronized.
See API documentation in annoMatrix.js.
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
addObsColumn(colSchema, Ctor, value) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = (this as any).annoMatrix.addObsColumn(
colSchema,
Ctor,
value
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const obsCrossfilter = (this as any).obsCrossfilter.setData(
annoMatrix._cache.obs
);
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
dropObsColumn(col) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = (this as any).annoMatrix.dropObsColumn(col);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
const dimName = _dimensionName("obs", col);
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'oldCol' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
renameObsColumn(oldCol, newCol) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = (this as any).annoMatrix.renameObsColumn(oldCol, newCol);
const oldDimName = _dimensionName("obs", oldCol);
const newDimName = _dimensionName("obs", newCol);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(oldDimName)) {
obsCrossfilter = obsCrossfilter.renameDimension(oldDimName, newDimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
addObsAnnoCategory(col, category) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = (this as any).annoMatrix.addObsAnnoCategory(
col,
category
);
const dimName = _dimensionName("obs", col);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async removeObsAnnoCategory(col, category, unassignedCategory) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = await (this as any).annoMatrix.removeObsAnnoCategory(
col,
category,
unassignedCategory
);
const dimName = _dimensionName("obs", col);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async setObsColumnValues(col, rowLabels, value) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = await (this as any).annoMatrix.setObsColumnValues(
col,
rowLabels,
value
);
const dimName = _dimensionName("obs", col);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async resetObsColumnValues(col, oldValue, newValue) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = await (this as any).annoMatrix.resetObsColumnValues(
col,
oldValue,
newValue
);
const dimName = _dimensionName("obs", col);
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
addEmbedding(colSchema) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const annoMatrix = (this as any).annoMatrix.addEmbedding(colSchema);
return new AnnoMatrixObsCrossfilter(
annoMatrix,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter
);
}
/**
* Drop the crossfilter dimension. Do not change the annoMatrix. Useful when we
* want to stop trackin the selection state, but aren't sure we want to blow the
* annomatrix cache.
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
dropDimension(field, query) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Anno... Remove this comment to see the full error message
const { annoMatrix } = this;
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
const keys = annoMatrix
.getCacheKeys(field, query)
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'k' implicitly has an 'any' type.
.filter((k) => k !== undefined);
const dimName = _dimensionName(field, keys);
if (obsCrossfilter.hasDimension(dimName)) {
obsCrossfilter = obsCrossfilter.delDimension(dimName);
}
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
/**
Selection state - API is identical to ImmutableTypedCrossfilter, as these
are just wrappers to lazy create indices.
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async select(field, query, spec) {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Anno... Remove this comment to see the full error message
const { annoMatrix } = this;
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
let { obsCrossfilter } = this;
if (!annoMatrix?._cache?.[field]) {
throw new Error("Unknown field name");
}
if (field === "var") {
throw new Error("unable to obsSelect upon the var dimension");
}
// grab the data, so we can grab the index.
const df = await annoMatrix.fetch(field, query);
const dimName = _dimensionNameFromDf(field, df);
if (!obsCrossfilter.hasDimension(dimName)) {
// lazy index generation - add dimension when first used
obsCrossfilter = this._addObsCrossfilterDimension(
annoMatrix,
obsCrossfilter,
field,
df
);
}
// select
obsCrossfilter = obsCrossfilter.select(dimName, spec);
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
selectAll() {
/*
Select all on any dimension in this field.
*/
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Anno... Remove this comment to see the full error message
const { annoMatrix } = this;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const currentDims = (this as any).obsCrossfilter.dimensionNames();
const obsCrossfilter = currentDims.reduce(
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'xfltr' implicitly has an 'any' type.
(xfltr, dim) => xfltr.select(dim, { mode: "all" }),
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter
); // eslint-disable-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
countSelected() {
/* if no data yet indexed in the crossfilter, just say everything is selected */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
if ((this as any).obsCrossfilter.size() === 0)
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).annoMatrix.nObs;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).obsCrossfilter.countSelected();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
allSelectedMask() {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter.size() === 0 ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter.dimensionNames().length === 0
) {
/* fake the mask */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return new Uint8Array((this as any).annoMatrix.nObs).fill(1);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).obsCrossfilter.allSelectedMask();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
allSelectedLabels() {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter.size() === 0 ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter.dimensionNames().length === 0
) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).annoMatrix.rowIndex.labels();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const mask = (this as any).obsCrossfilter.allSelectedMask();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const index = (this as any).annoMatrix.rowIndex.isubsetMask(mask);
return index.labels();
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'array' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
fillByIsSelected(array, selectedValue, deselectedValue) {
/* if no data yet indexed in the crossfilter, just say everything is selected */
if (
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter.size() === 0 ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).obsCrossfilter.dimensionNames().length === 0
) {
return array.fill(selectedValue);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).obsCrossfilter.fillByIsSelected(
array,
selectedValue,
deselectedValue
);
}
/**
** Private below
**/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_addObsCrossfilterDimension(annoMatrix, obsCrossfilter, field, df) {
if (field === "var") return obsCrossfilter;
const dimName = _dimensionNameFromDf(field, df);
const dimParams = this._getObsDimensionParams(field, df);
obsCrossfilter = obsCrossfilter.setData(annoMatrix._cache.obs);
// @ts-expect-error ts-migrate(2488) FIXME: Type 'any[] | undefined' must have a '[Symbol.iter... Remove this comment to see the full error message
obsCrossfilter = obsCrossfilter.addDimension(dimName, ...dimParams);
return obsCrossfilter;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_getColumnBaseType(field, col) {
/* Look up the primitive type for this field/col */
const colSchema = _getColumnSchema(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).annoMatrix.schema,
field,
col
);
return colSchema.type;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
_getObsDimensionParams(field, df) {
/* return the crossfilter dimensiontype type and params for this field/dataframe */
if (field === "emb") {
/* assumed to be 2D */
return ["spatial", df.icol(0).asArray(), df.icol(1).asArray()];
}
/* assumed to be 1D */
const col = df.icol(0);
const colName = df.colIndex.getLabel(0);
const type = this._getColumnBaseType(field, colName);
if (type === "string" || type === "categorical" || type === "boolean") {
return ["enum", col.asArray()];
}
if (type === "int32") {
return ["scalar", col.asArray(), Int32Array];
}
if (type === "float32") {
return ["scalar", col.asArray(), Float32Array];
}
// Currently not supporting boolean and categorical types.
console.error(
`Warning - unknown metadata schema (${type}) for field ${field} ${colName}.`
);
// skip it - we don't know what to do with this type
return undefined;
}
}
+25
View File
@@ -0,0 +1,25 @@
export { doBinaryRequest, doFetch } from "../util/actionHelpers";
/* double URI encode - needed for query-param filters */
export function _dubEncURIComp(s) {
return encodeURIComponent(encodeURIComponent(s));
}
/* currently unused, consider deleting */
export function _fetchResult(promise) {
let _status = "pending";
const res = promise.then(
(r) => {
_status = "success";
return r;
},
(e) => {
_status = "error";
throw e;
}
);
res.status = () => _status;
return res;
}
-29
View File
@@ -1,29 +0,0 @@
export { doBinaryRequest, doFetch } from "../util/actionHelpers";
/* double URI encode - needed for query-param filters */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _dubEncURIComp(s: any) {
return encodeURIComponent(encodeURIComponent(s));
}
/* currently unused, consider deleting */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _fetchResult(promise: any) {
let _status = "pending";
const res = promise.then(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(r: any) => {
_status = "success";
return r;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(e: any) => {
_status = "error";
throw e;
}
);
res.status = () => _status;
return res;
}
@@ -27,9 +27,6 @@ import {
const promiseThrottle = new PromiseLimit(5);
export default class AnnoMatrixLoader extends AnnoMatrix {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
baseURL: any;
/*
AnnoMatrix implementation which proxies to HTTP server using the CXG REST API.
Used as the base (non-view) instance.
@@ -40,8 +37,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
new AnnoMatrixLoader(serverBaseURL, schema) -> instance
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
constructor(baseURL: any, schema: any) {
constructor(baseURL, schema) {
const { nObs, nVar } = schema.dataframe;
super(schema, nObs, nVar);
@@ -56,8 +52,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
/**
** Public. API described in base class.
**/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
addObsAnnoCategory(col: any, category: any) {
addObsAnnoCategory(col, category) {
/*
Add a new category (aka label) to the schema for an obs column.
*/
@@ -69,15 +64,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async removeObsAnnoCategory(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
col: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
category: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
unassignedCategory: any
) {
async removeObsAnnoCategory(col, category, unassignedCategory) {
/*
Remove a single "category" (aka "label") from the data & schema of an obs column.
*/
@@ -97,8 +84,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dropObsColumn(col: any) {
dropObsColumn(col) {
/*
drop column from field
*/
@@ -106,14 +92,11 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
_writableCheck(colSchema); // throws on error
const newAnnoMatrix = this._clone();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newAnnoMatrix._cache.obs = (this as any)._cache.obs.dropCol(col);
newAnnoMatrix._cache.obs = this._cache.obs.dropCol(col);
newAnnoMatrix.schema = removeObsAnnoColumn(this.schema, col);
return newAnnoMatrix;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
addObsColumn(colSchema, Ctor, value) {
/*
add a column to field, initializing with value. Value may
@@ -126,8 +109,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
const colName = colSchema.name;
if (
_getColumnSchema(this.schema, "obs", colName) ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any)._cache.obs.hasCol(colName)
this._cache.obs.hasCol(colName)
) {
throw new Error("column already exists");
}
@@ -143,8 +125,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
} else {
data = new Ctor(this.nObs).fill(value);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newAnnoMatrix._cache.obs = (this as any)._cache.obs.withCol(colName, data);
newAnnoMatrix._cache.obs = this._cache.obs.withCol(colName, data);
normalizeWritableCategoricalSchema(
colSchema,
newAnnoMatrix._cache.obs.col(colName)
@@ -153,18 +134,15 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
return newAnnoMatrix;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'oldCol' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
renameObsColumn(oldCol, newCol) {
/*
Rename the obs oldColName to newColName. oldCol must be writable.
*/
const oldColSchema = _getColumnSchema(this.schema, "obs", oldCol);
_writableCheck(oldColSchema);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const value = (this as any)._cache.obs.hasCol(oldCol)
? // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any)._cache.obs.col(oldCol).asArray()
_writableCheck(oldColSchema); // throws on error
const value = this._cache.obs.hasCol(oldCol)
? this._cache.obs.col(oldCol).asArray()
: undefined;
return this.dropObsColumn(oldCol).addObsColumn(
{
@@ -176,8 +154,6 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async setObsColumnValues(col, rowLabels, value) {
/*
Set all rows identified by rowLabels to value.
@@ -187,13 +163,11 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
// ensure that we have the data in cache before we manipulate it
await this.fetch("obs", col);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
if (!(this as any)._cache.obs.hasCol(col))
if (!this._cache.obs.hasCol(col))
throw new Error("Internal error - user annotation data missing");
const rowIndices = this.rowIndex.getOffsets(rowLabels);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const data = (this as any)._cache.obs.col(col).asArray().slice();
const data = this._cache.obs.col(col).asArray().slice();
for (let i = 0, len = rowIndices.length; i < len; i += 1) {
const idx = rowIndices[i];
if (idx === undefined) throw new Error("Unknown row label");
@@ -201,11 +175,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
}
const newAnnoMatrix = this._clone();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newAnnoMatrix._cache.obs = (this as any)._cache.obs.replaceColData(
col,
data
);
newAnnoMatrix._cache.obs = this._cache.obs.replaceColData(col, data);
const { categories } = colSchema;
if (!categories?.includes(value)) {
newAnnoMatrix.schema = addObsAnnoCategory(this.schema, col, value);
@@ -213,8 +183,6 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
return newAnnoMatrix;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async resetObsColumnValues(col, oldValue, newValue) {
/*
Set all rows with value 'oldValue' to 'newValue'.
@@ -228,22 +196,16 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
// ensure that we have the data in cache before we manipulate it
await this.fetch("obs", col);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
if (!(this as any)._cache.obs.hasCol(col))
if (!this._cache.obs.hasCol(col))
throw new Error("Internal error - user annotation data missing");
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const data = (this as any)._cache.obs.col(col).asArray().slice();
const data = this._cache.obs.col(col).asArray().slice();
for (let i = 0, l = data.length; i < l; i += 1) {
if (data[i] === oldValue) data[i] = newValue;
}
const newAnnoMatrix = this._clone();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newAnnoMatrix._cache.obs = (this as any)._cache.obs.replaceColData(
col,
data
);
newAnnoMatrix._cache.obs = this._cache.obs.replaceColData(col, data);
const { categories } = colSchema;
if (!categories?.includes(newValue)) {
newAnnoMatrix.schema = addObsAnnoCategory(this.schema, col, newValue);
@@ -251,8 +213,6 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
return newAnnoMatrix;
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
addEmbedding(colSchema) {
/*
add new layout to the obs embeddings
@@ -270,8 +230,6 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
/**
** Private below
**/
// @ts-expect-error ts-migrate(2416) FIXME: Property '_doLoad' in type 'AnnoMatrixLoader' is n... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async _doLoad(field, query) {
/*
_doLoad - evaluates the query against the field. Returns:
@@ -310,7 +268,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
result.colIndex.labels()
);
result = normalizeResponse(field, this.schema, result);
result = normalizeResponse(field, query, this.schema, result);
return [whereCacheUpdate, result];
}
@@ -320,14 +278,12 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
Utility functions below
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
function _writableCheck(colSchema) {
if (!colSchema?.writable) {
throw new Error("Unknown or readonly obs column");
}
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
function _writableCategoryTypeCheck(colSchema) {
_writableCheck(colSchema);
if (colSchema.type !== "categorical") {
@@ -335,7 +291,6 @@ function _writableCategoryTypeCheck(colSchema) {
}
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'baseURL' implicitly has an 'any' type.
function _embLoader(baseURL, _field, query) {
_expectSimpleQuery(query);
@@ -345,7 +300,6 @@ function _embLoader(baseURL, _field, query) {
return () => doBinaryRequest(url);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'baseURL' implicitly has an 'any' type.
function _obsOrVarLoader(baseURL, field, query) {
_expectSimpleQuery(query);
@@ -355,7 +309,6 @@ function _obsOrVarLoader(baseURL, field, query) {
return () => doBinaryRequest(url);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'baseURL' implicitly has an 'any' type.
function _XLoader(baseURL, field, query) {
_expectComplexQuery(query);
@@ -11,8 +11,7 @@ Undoable metareducer and the AnnoMatrix private API. It would be helpful
to make the Undoable interface better factored.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
const annoMatrixGC = (store: any) => (next: any) => (action: any) => {
const annoMatrixGC = (store) => (next) => (action) => {
if (_itIsTimeForGC()) {
_doGC(store);
}
@@ -35,8 +34,7 @@ function _itIsTimeForGC() {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _doGC(store: any) {
function _doGC(store) {
const state = store.getState();
// these should probably be a function imported from undoable.js, etc, as
@@ -45,10 +43,8 @@ function _doGC(store: any) {
const undoableFuture = state["@@undoable/future"];
const undoableStack = undoablePast
.concat(undoableFuture)
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.flatMap((snapshot: any) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
snapshot.filter((v: any) => v[0] === "annoMatrix").map((v: any) => v[1])
.flatMap((snapshot) =>
snapshot.filter((v) => v[0] === "annoMatrix").map((v) => v[1])
);
const currentAnnoMatrix = state.annoMatrix;
@@ -57,18 +53,14 @@ function _doGC(store: any) {
as our current gc algo is more aggressive with those not hot.
*/
const allAnnoMatrices = new Map(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
undoableStack.map((m: any) => [m, { isHot: false }])
undoableStack.map((m) => [m, { isHot: false }])
);
let am = currentAnnoMatrix;
while (am) {
allAnnoMatrices.set(am, { isHot: true });
am = am.viewOf;
}
allAnnoMatrices.forEach((hints, annoMatrix) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(annoMatrix as any)._gc(hints)
);
allAnnoMatrices.forEach((hints, annoMatrix) => annoMatrix._gc(hints));
}
export default annoMatrixGC;
@@ -5,14 +5,8 @@ import {
overflowCategoryLabel,
globalConfig,
} from "../globals";
import { Dataframe } from "../util/dataframe";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function normalizeResponse(
field: string,
schema: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
response: Dataframe
): Dataframe {
export function normalizeResponse(field, query, schema, response) {
/**
* There are a number of assumptions in the front-end about data typing and data
* characteristics. This routine will normalize a server response dataframe
@@ -65,8 +59,7 @@ export function normalizeResponse(
return response;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
function castColumnToBoolean(df: Dataframe, label: any): Dataframe {
function castColumnToBoolean(df, label) {
const colData = df.col(label).asArray();
const newColData = new Array(colData.length);
for (let i = 0; i < colData.length; i += 1) newColData[i] = !!colData[i];
@@ -74,11 +67,10 @@ function castColumnToBoolean(df: Dataframe, label: any): Dataframe {
return df;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function normalizeWritableCategoricalSchema(colSchema: any, col: any) {
export function normalizeWritableCategoricalSchema(colSchema, col) {
/*
Ensure all enum writable / categorical schema have a categories array, that
the categories array contains all unique values in the data array, AND that
the categories array contains all unique values in the data array, AND that
the array is UI sorted.
*/
const categorySet = new Set(
@@ -91,15 +83,9 @@ export function normalizeWritableCategoricalSchema(colSchema: any, col: any) {
return colSchema;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function normalizeCategorical(
df: Dataframe,
colLabel: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colSchema: any // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) {
export function normalizeCategorical(df, colLabel, colSchema) {
/*
If writable, ensure schema matches data and we have an unassigned label
If not writable, ensure schema matches data and that we consolidate labels in excess
of "top N" into an overflow labels.
*/
@@ -10,8 +10,7 @@ import { _dubEncURIComp } from "./fetchHelpers";
* @param {object | string} query - the query
* @returns {object | string} - the normalized query
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _queryValidate(query: any) {
export function _queryValidate(query) {
if (typeof query !== "object") return query;
if (query.where && query.summarize)
@@ -41,13 +40,11 @@ export function _queryValidate(query: any) {
throw new Error("query must specify one of where or summarize");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _expectSimpleQuery(query: any) {
export function _expectSimpleQuery(query) {
if (typeof query === "object") throw new Error("expected simple query");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _expectComplexQuery(query: any) {
export function _expectComplexQuery(query) {
if (typeof query !== "object") throw new Error("expected complex query");
}
@@ -58,8 +55,7 @@ export function _expectComplexQuery(query: any) {
* @param {string|object} query
* @returns the key
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _queryCacheKey(field: any, query: any) {
export function _queryCacheKey(field, query) {
if (typeof query === "object") {
// complex query
if (query.where) {
@@ -88,25 +84,22 @@ export function _queryCacheKey(field: any, query: any) {
return `${field}/${query}`;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _urlEncodeWhereQuery(q: any) {
function _urlEncodeWhereQuery(q) {
const { field: queryField, column: queryColumn, value: queryValue } = q;
return `${_dubEncURIComp(queryField)}:${_dubEncURIComp(
queryColumn
)}=${_dubEncURIComp(queryValue)}`;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _urlEncodeSummarizeQuery(q: any) {
function _urlEncodeSummarizeQuery(q) {
const { method, field, column, values } = q;
const filter = values // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.map((value: any) => _urlEncodeWhereQuery({ field, column, value }))
const filter = values
.map((value) => _urlEncodeWhereQuery({ field, column, value }))
.join("&");
return `method=${method}&${filter}`;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _urlEncodeComplexQuery(q: any) {
export function _urlEncodeComplexQuery(q) {
if (typeof q === "object") {
if (q.where) {
return _urlEncodeWhereQuery(q.where);
@@ -118,8 +111,7 @@ export function _urlEncodeComplexQuery(q: any) {
throw new Error("Unrecognized complex query type");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _urlEncodeLabelQuery(colKey: any, q: any) {
export function _urlEncodeLabelQuery(colKey, q) {
if (!colKey) throw new Error("Unsupported query by name");
if (typeof q !== "string") throw new Error("Query must be a simple label.");
return `${colKey}=${encodeURIComponent(q)}`;
@@ -128,8 +120,7 @@ export function _urlEncodeLabelQuery(colKey: any, q: any) {
/**
* Generate the column key the server will send us for this query.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _hashStringValues(arrayOfString: any) {
export function _hashStringValues(arrayOfString) {
const hash = sha1(arrayOfString.join(""));
return hash;
}
@@ -1,8 +1,7 @@
/*
Private helper functions related to schema
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _getColumnSchema(schema: any, field: any, col: any) {
export function _getColumnSchema(schema, field, col) {
/* look up the column definition */
switch (field) {
case "obs":
@@ -24,14 +23,12 @@ export function _getColumnSchema(schema: any, field: any, col: any) {
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function _isIndex(schema: any, field: any, col: any): boolean {
export function _isIndex(schema, field, col) {
const index = schema.annotations?.[field].index;
return index && index === col;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
export function _getColumnDimensionNames(schema: any, field: any, col: any) {
export function _getColumnDimensionNames(schema, field, col) {
/*
field/col may be an alias for multiple columns. Currently used to map ND
values to 1D dataframe columns for embeddings/layout. Signified by the presence
@@ -44,8 +41,6 @@ export function _getColumnDimensionNames(schema: any, field: any, col: any) {
return colSchema.dims || [col];
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function _schemaColumns(schema, field) {
switch (field) {
case "obs":
@@ -59,21 +54,13 @@ export function _schemaColumns(schema, field) {
}
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function _getWritableColumns(schema, field) {
if (field !== "obs") return [];
return (
schema.annotations.obs.columns
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'v' implicitly has an 'any' type.
.filter((v) => v.writable)
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'v' implicitly has an 'any' type.
.map((v) => v.name)
);
return schema.annotations.obs.columns
.filter((v) => v.writable)
.map((v) => v.name);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function _isContinuousType(schema) {
const { type } = schema;
return !(type === "string" || type === "boolean" || type === "categorical");
@@ -5,8 +5,7 @@ instances of AnnoMatrix, implementing common UI functions.
import { AnnoMatrixRowSubsetView, AnnoMatrixClipView } from "./views";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function isubsetMask(annoMatrix: any, obsMask: any) {
export function isubsetMask(annoMatrix, obsMask) {
/*
Subset annomatrix to contain the rows which have truish value in the mask.
Maks length must equal annoMatrix.nObs (row count).
@@ -14,8 +13,6 @@ export function isubsetMask(annoMatrix: any, obsMask: any) {
return isubset(annoMatrix, _maskToList(obsMask));
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function isubset(annoMatrix, obsOffsets) {
/*
Subset annomatrix to contain the positions contained in the obsOffsets array
@@ -28,8 +25,6 @@ export function isubset(annoMatrix, obsOffsets) {
return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function subset(annoMatrix, obsLabels) {
/*
subset based on labels
@@ -38,8 +33,6 @@ export function subset(annoMatrix, obsLabels) {
return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function subsetByIndex(annoMatrix, obsIndex) {
/*
subset based upon the new obs index.
@@ -47,8 +40,6 @@ export function subsetByIndex(annoMatrix, obsIndex) {
return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function clip(annoMatrix, qmin, qmax) {
/*
Create a view that clips all continuous data to the [min, max] range.
@@ -62,7 +53,6 @@ export function clip(annoMatrix, qmin, qmax) {
Private utility functions below
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'mask' implicitly has an 'any' type.
function _maskToList(mask) {
/* convert masks to lists - method wastes space, but is fast */
if (!mask) {
@@ -9,31 +9,21 @@ import { _whereCacheCreate } from "./whereCache";
import { _isContinuousType, _getColumnSchema } from "./schema";
class AnnoMatrixView extends AnnoMatrix {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
constructor(viewOf: any, rowIndex = null) {
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
constructor(viewOf, rowIndex = null) {
const nObs = rowIndex ? rowIndex.size() : viewOf.nObs;
super(viewOf.schema, nObs, viewOf.nVar, rowIndex || viewOf.rowIndex);
this.viewOf = viewOf;
this.isView = true;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
addObsAnnoCategory(col: any, category: any) {
addObsAnnoCategory(col, category) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = this.viewOf.addObsAnnoCategory(col, category);
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
return newAnnoMatrix;
}
async removeObsAnnoCategory(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
col: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
category: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
unassignedCategory: any
) {
async removeObsAnnoCategory(col, category, unassignedCategory) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = await this.viewOf.removeObsAnnoCategory(
col,
@@ -44,8 +34,7 @@ class AnnoMatrixView extends AnnoMatrix {
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
dropObsColumn(col: any) {
dropObsColumn(col) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = this.viewOf.dropObsColumn(col);
newAnnoMatrix._cache.obs = this._cache.obs.dropCol(col);
@@ -53,24 +42,21 @@ class AnnoMatrixView extends AnnoMatrix {
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
addObsColumn(colSchema: any, Ctor: any, value: any) {
addObsColumn(colSchema, Ctor, value) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = this.viewOf.addObsColumn(colSchema, Ctor, value);
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
renameObsColumn(oldCol: any, newCol: any) {
renameObsColumn(oldCol, newCol) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = this.viewOf.renameObsColumn(oldCol, newCol);
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async setObsColumnValues(col: any, rowLabels: any, value: any) {
async setObsColumnValues(col, rowLabels, value) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = await this.viewOf.setObsColumnValues(
col,
@@ -82,8 +68,7 @@ class AnnoMatrixView extends AnnoMatrix {
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
async resetObsColumnValues(col: any, oldValue: any, newValue: any) {
async resetObsColumnValues(col, oldValue, newValue) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = await this.viewOf.resetObsColumnValues(
col,
@@ -95,8 +80,7 @@ class AnnoMatrixView extends AnnoMatrix {
return newAnnoMatrix;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
addEmbedding(colSchema: any) {
addEmbedding(colSchema) {
const newAnnoMatrix = this._clone();
newAnnoMatrix.viewOf = this.viewOf.addEmbedding(colSchema);
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
@@ -108,22 +92,17 @@ class AnnoMatrixMapView extends AnnoMatrixView {
/*
A view which knows how to transform its data.
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'viewOf' implicitly has an 'any' type.
constructor(viewOf, mapFn) {
super(viewOf);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).mapFn = mapFn;
this.mapFn = mapFn;
}
// @ts-expect-error ts-migrate(2416) FIXME: Property '_doLoad' in type 'AnnoMatrixMapView' is ... Remove this comment to see the full error message
async _doLoad(field, query) {
const df = await this.viewOf._fetch(field, query);
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colData' implicitly has an 'any' type.
const dfMapped = df.mapColumns((colData, colIdx) => {
const colLabel = df.colIndex.getLabel(colIdx);
const colSchema = _getColumnSchema(this.schema, field, colLabel);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (this as any).mapFn(field, colLabel, colSchema, colData, df);
return this.mapFn(field, colLabel, colSchema, colData, df);
});
const whereCacheUpdate = _whereCacheCreate(
field,
@@ -138,17 +117,12 @@ export class AnnoMatrixClipView extends AnnoMatrixMapView {
/*
A view which is a clipped transformation of its parent
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'viewOf' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
constructor(viewOf, qmin, qmax) {
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
super(viewOf, (field, colLabel, colSchema, colData, df) =>
_clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax)
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).isClipped = true;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(this as any).clipRange = [qmin, qmax];
this.isClipped = true;
this.clipRange = [qmin, qmax];
Object.seal(this);
}
}
@@ -157,15 +131,11 @@ export class AnnoMatrixRowSubsetView extends AnnoMatrixView {
/*
A view which is a subset of total rows.
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'viewOf' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
constructor(viewOf, rowIndex) {
super(viewOf, rowIndex);
Object.seal(this);
}
// @ts-expect-error ts-migrate(2416) FIXME: Property '_doLoad' in type 'AnnoMatrixRowSubsetVie... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
async _doLoad(field, query) {
const df = await this.viewOf._fetch(field, query);
@@ -188,7 +158,6 @@ export class AnnoMatrixRowSubsetView extends AnnoMatrixView {
Utility functions below
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
function _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax) {
/* only clip obs and var scalar columns */
if (field !== "obs" && field !== "X") return colData;
@@ -51,17 +51,7 @@ creates a cache entry of:
import { _getColumnDimensionNames } from "./schema";
import { _hashStringValues } from "./query";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function _whereCacheGet(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
whereCache: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
schema: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
field: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
query: any
) {
export function _whereCacheGet(whereCache, schema, field, query) {
/*
query will either be an where query (object) or a column name (string).
@@ -95,8 +85,6 @@ export function _whereCacheGet(
return _getColumnDimensionNames(schema, field, query) ?? [undefined];
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function _whereCacheCreate(field, query, columnLabels) {
/*
Create a new whereCache
@@ -143,12 +131,10 @@ export function _whereCacheCreate(field, query, columnLabels) {
return {};
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'dst' implicitly has an 'any' type.
function __mergeQueries(dst, src) {
for (const [queryField, columnMap] of Object.entries(src)) {
dst[queryField] = dst[queryField] || new Map();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
for (const [queryColumn, valueMap] of columnMap as any) {
for (const [queryColumn, valueMap] of columnMap) {
if (!dst[queryField].has(queryColumn))
dst[queryField].set(queryColumn, new Map());
for (const [queryValue, columnLabels] of valueMap) {
@@ -158,7 +144,6 @@ function __mergeQueries(dst, src) {
}
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'dst' implicitly has an 'any' type.
function __whereCacheMerge(dst, src) {
/*
merge src into dst (modifies dst)
@@ -176,7 +161,6 @@ function __whereCacheMerge(dst, src) {
dst.summarize = dst.summarize || {};
for (const [field, method] of Object.entries(src.summarize)) {
dst.summarize[field] = dst.summarize[field] || {};
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
for (const [methodName, query] of Object.entries(method)) {
dst.summarize[field][methodName] =
dst.summarize[field][methodName] || {};
@@ -187,8 +171,6 @@ function __whereCacheMerge(dst, src) {
return dst;
}
// @ts-expect-error ts-migrate(7019) FIXME: Rest parameter 'caches' implicitly has an 'any[]' ... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function _whereCacheMerge(...caches) {
return caches.reduce(__whereCacheMerge, {});
}

Some files were not shown because too many files have changed in this diff Show More