Skip to content

Commit 989af83

Browse files
authored
Support scope_prefix parameter in link_views_by_dict (#325)
* Support scope_prefix parameter in link_views_by_dict * Lint * Remove unused import * Exports * Coverage * Version bump
1 parent 7b1458b commit 989af83

File tree

6 files changed

+168
-7
lines changed

6 files changed

+168
-7
lines changed

.coveragerc_omit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ omit =
66
vitessce/widget.py
77
vitessce/wrappers.py
88
vitessce/repr.py
9+
vitessce/utils.py
910
vitessce/data_utils/anndata.py
1011
vitessce/data_utils/ome.py
1112
vitessce/data_utils/entities.py

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "vitessce"
7-
version = "3.2.2"
7+
version = "3.2.3"
88
authors = [
99
{ name="Mark Keller", email="[email protected]" },
1010
]

tests/test_config.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,3 +1402,115 @@ def test_config_link_views_by_dict():
14021402
],
14031403
"initStrategy": "auto"
14041404
}
1405+
1406+
1407+
def test_config_link_views_by_dict_with_scope_prefix():
1408+
vc = VitessceConfig(schema_version="1.0.16", name="My config")
1409+
dataset = vc.add_dataset(name="My dataset")
1410+
1411+
spatial_view = vc.add_view('spatial', dataset=dataset)
1412+
lc_view = vc.add_view('layerController', dataset=dataset)
1413+
1414+
vc.link_views_by_dict([spatial_view, lc_view], {
1415+
'spatialImageLayer': CL([
1416+
{
1417+
'spatialLayerOpacity': 1,
1418+
'spatialImageChannel': CL([
1419+
{
1420+
'spatialTargetC': 0,
1421+
'spatialChannelColor': [255, 0, 0],
1422+
},
1423+
{
1424+
'spatialTargetC': 1,
1425+
'spatialChannelColor': [0, 255, 0],
1426+
},
1427+
]),
1428+
},
1429+
])
1430+
}, scope_prefix="SOME_PREFIX_")
1431+
1432+
vc_dict = vc.to_dict()
1433+
1434+
assert vc_dict == {
1435+
"version": "1.0.16",
1436+
"name": "My config",
1437+
"description": "",
1438+
"datasets": [
1439+
{
1440+
"uid": "A",
1441+
"name": "My dataset",
1442+
"files": []
1443+
}
1444+
],
1445+
"coordinationSpace": {
1446+
"dataset": {
1447+
"A": "A"
1448+
},
1449+
"spatialImageLayer": {
1450+
"SOME_PREFIX_0": "__dummy__"
1451+
},
1452+
"spatialLayerOpacity": {
1453+
"SOME_PREFIX_0": 1,
1454+
},
1455+
"spatialImageChannel": {
1456+
"SOME_PREFIX_0": "__dummy__",
1457+
"SOME_PREFIX_1": "__dummy__"
1458+
},
1459+
"spatialTargetC": {
1460+
"SOME_PREFIX_0": 0,
1461+
"SOME_PREFIX_1": 1
1462+
},
1463+
"spatialChannelColor": {
1464+
"SOME_PREFIX_0": [255, 0, 0],
1465+
"SOME_PREFIX_1": [0, 255, 0],
1466+
},
1467+
"metaCoordinationScopes": {
1468+
"SOME_PREFIX_0": {
1469+
"spatialImageLayer": ["SOME_PREFIX_0"]
1470+
}
1471+
},
1472+
"metaCoordinationScopesBy": {
1473+
"SOME_PREFIX_0": {
1474+
"spatialImageLayer": {
1475+
"spatialLayerOpacity": {
1476+
"SOME_PREFIX_0": "SOME_PREFIX_0"
1477+
},
1478+
"spatialImageChannel": {
1479+
"SOME_PREFIX_0": ["SOME_PREFIX_0", "SOME_PREFIX_1"]
1480+
}
1481+
},
1482+
"spatialImageChannel": {
1483+
"spatialTargetC": {
1484+
"SOME_PREFIX_0": "SOME_PREFIX_0",
1485+
"SOME_PREFIX_1": "SOME_PREFIX_1"
1486+
},
1487+
"spatialChannelColor": {
1488+
"SOME_PREFIX_0": "SOME_PREFIX_0",
1489+
"SOME_PREFIX_1": "SOME_PREFIX_1"
1490+
}
1491+
}
1492+
}
1493+
}
1494+
},
1495+
"layout": [
1496+
{
1497+
"component": "spatial",
1498+
"coordinationScopes": {
1499+
"dataset": "A",
1500+
"metaCoordinationScopes": ["SOME_PREFIX_0"],
1501+
"metaCoordinationScopesBy": ["SOME_PREFIX_0"]
1502+
},
1503+
"x": 0, "y": 0, "w": 1, "h": 1
1504+
},
1505+
{
1506+
"component": "layerController",
1507+
"coordinationScopes": {
1508+
"dataset": "A",
1509+
"metaCoordinationScopes": ["SOME_PREFIX_0"],
1510+
"metaCoordinationScopesBy": ["SOME_PREFIX_0"]
1511+
},
1512+
"x": 0, "y": 0, "w": 1, "h": 1
1513+
}
1514+
],
1515+
"initStrategy": "auto"
1516+
}

vitessce/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
CoordinationLevel,
1111
)
1212

13+
from .utils import (
14+
get_initial_coordination_scope_prefix,
15+
get_initial_coordination_scope_name,
16+
)
17+
1318
from .repr import make_repr
1419

1520
from .constants import (

vitessce/config.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
)
1313

1414
from .repr import make_repr, make_params_repr
15+
from .utils import create_prefixed_get_next_scope_numeric
1516

1617

1718
def _get_next_scope(prev_scopes):
@@ -862,6 +863,8 @@ def __init__(self, schema_version, name=None, description=None, base_dir=None):
862863
"initStrategy": "auto"
863864
}
864865

866+
self.get_next_scope = _get_next_scope
867+
865868
if name is None:
866869
self.config["name"] = ""
867870
else:
@@ -926,7 +929,7 @@ def add_dataset(self, name="", uid=None, files=None, objs=None):
926929
)
927930
)
928931
"""
929-
uid = uid if uid is not None else _get_next_scope(
932+
uid = uid if uid is not None else self.get_next_scope(
930933
[d.dataset['uid'] for d in self.config["datasets"]])
931934
assert isinstance(uid, str)
932935
vcd = VitessceConfigDataset(uid, name, base_dir=self.base_dir)
@@ -1099,7 +1102,7 @@ def add_coordination(self, *c_types):
10991102
prev_scopes = list(self.config["coordinationSpace"][c_type_str].keys(
11001103
)) if c_type_str in self.config["coordinationSpace"].keys() else []
11011104
scope = VitessceConfigCoordinationScope(
1102-
c_type_str, _get_next_scope(prev_scopes))
1105+
c_type_str, self.get_next_scope(prev_scopes))
11031106
if scope.c_type not in self.config["coordinationSpace"]:
11041107
self.config["coordinationSpace"][scope.c_type] = {}
11051108
self.config["coordinationSpace"][scope.c_type][scope.c_scope] = scope
@@ -1138,8 +1141,8 @@ def add_meta_coordination(self):
11381141
prev_meta_by_scopes = self.config["coordinationSpace"].get(ct.META_COORDINATION_SCOPES_BY.value, {}).keys()
11391142

11401143
meta_container = VitessceConfigMetaCoordinationScope(
1141-
_get_next_scope(prev_meta_scopes),
1142-
_get_next_scope(prev_meta_by_scopes),
1144+
self.get_next_scope(prev_meta_scopes),
1145+
self.get_next_scope(prev_meta_by_scopes),
11431146
)
11441147
if ct.META_COORDINATION_SCOPES.value not in self.config["coordinationSpace"]:
11451148
self.config["coordinationSpace"][ct.META_COORDINATION_SCOPES.value] = {}
@@ -1256,6 +1259,8 @@ def add_coordination_by_dict(self, input_val):
12561259
"""
12571260
def process_level(level):
12581261
result = {}
1262+
if level is None:
1263+
return result
12591264
for c_type, next_level_or_initial_value in level.items():
12601265
c_type_str = norm_enum(c_type, ct)
12611266
# Check if value of object is instanceof CoordinationLevel
@@ -1295,7 +1300,7 @@ def map_func(next_el):
12951300
output_val = process_level(input_val)
12961301
return output_val
12971302

1298-
def link_views_by_dict(self, views, input_val, meta=True):
1303+
def link_views_by_dict(self, views, input_val, meta=True, scope_prefix=None):
12991304
"""
13001305
A convenience function for setting up multi-level and meta-coordination scopes across a set of views.
13011306
@@ -1322,6 +1327,8 @@ def link_views_by_dict(self, views, input_val, meta=True):
13221327
ct.SPATIAL_TARGET_Y: 0,
13231328
})
13241329
"""
1330+
if scope_prefix:
1331+
self.get_next_scope = create_prefixed_get_next_scope_numeric(scope_prefix)
13251332
scopes = self.add_coordination_by_dict(input_val)
13261333
if meta:
13271334
meta_scope = self.add_meta_coordination()
@@ -1332,7 +1339,8 @@ def link_views_by_dict(self, views, input_val, meta=True):
13321339
else:
13331340
for view in views:
13341341
view.use_coordination_by_dict(scopes)
1335-
1342+
if scope_prefix:
1343+
self.get_next_scope = _get_next_scope
13361344
return self
13371345

13381346
def set_coordination_value(self, c_type, c_scope, c_value):

vitessce/utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
def get_next_scope_numeric(prev_scopes):
2+
next_scope_int = 0
3+
next_scope_str = None
4+
5+
while True:
6+
next_scope_str = str(next_scope_int)
7+
if next_scope_str not in prev_scopes:
8+
break
9+
next_scope_int += 1
10+
return next_scope_str
11+
12+
13+
def create_prefixed_get_next_scope_numeric(prefix):
14+
15+
def inner_get_next_scope(prev_scopes):
16+
next_scope_int = 0
17+
next_scope_str = None
18+
19+
while True:
20+
next_scope_str = f"{prefix}{next_scope_int}"
21+
if next_scope_str not in prev_scopes:
22+
break
23+
next_scope_int += 1
24+
return next_scope_str
25+
26+
return inner_get_next_scope
27+
28+
29+
def get_initial_coordination_scope_prefix(dataset_uid, data_type):
30+
return f"init_{dataset_uid}_{data_type}_"
31+
32+
33+
def get_initial_coordination_scope_name(dataset_uid, data_type, i=None):
34+
prefix = get_initial_coordination_scope_prefix(dataset_uid, data_type)
35+
return f"{prefix}{0 if i is None else i}"

0 commit comments

Comments
 (0)