portal-visualization 0.4.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- portal_visualization/__init__.py +36 -0
- portal_visualization/assays.py +14 -0
- portal_visualization/builder_factory.py +256 -0
- portal_visualization/builders/__init__.py +1 -0
- portal_visualization/builders/anndata_builders.py +870 -0
- portal_visualization/builders/base_builders.py +133 -0
- portal_visualization/builders/epic_builders.py +208 -0
- portal_visualization/builders/imaging_builders.py +512 -0
- portal_visualization/builders/object_by_analyte_builders.py +300 -0
- portal_visualization/builders/scatterplot_builders.py +112 -0
- portal_visualization/builders/sprm_builders.py +366 -0
- portal_visualization/cli.py +152 -0
- portal_visualization/client.py +540 -0
- portal_visualization/constants.py +12 -0
- portal_visualization/defaults.json +22 -0
- portal_visualization/epic_factory.py +18 -0
- portal_visualization/mock_client.py +50 -0
- portal_visualization/paths.py +19 -0
- portal_visualization/utils.py +299 -0
- portal_visualization-0.4.20.dist-info/METADATA +360 -0
- portal_visualization-0.4.20.dist-info/RECORD +25 -0
- portal_visualization-0.4.20.dist-info/WHEEL +5 -0
- portal_visualization-0.4.20.dist-info/entry_points.txt +2 -0
- portal_visualization-0.4.20.dist-info/licenses/LICENSE +21 -0
- portal_visualization-0.4.20.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Portal Visualization - HuBMAP Dataset Visualization Configuration Generator.
|
|
2
|
+
|
|
3
|
+
This package provides two install modes:
|
|
4
|
+
|
|
5
|
+
1. **Thin Install** (default): `pip install portal-visualization`
|
|
6
|
+
- Provides only the has_visualization() and process_hints() functions
|
|
7
|
+
- No heavy dependencies (vitessce, zarr, etc.)
|
|
8
|
+
- Use for checking if a dataset has visualization support
|
|
9
|
+
- Very lightweight install
|
|
10
|
+
- Does not support visualization generation
|
|
11
|
+
|
|
12
|
+
2. **Full Install**: `pip install portal-visualization[full]`
|
|
13
|
+
- Provides complete visualization generation capabilities
|
|
14
|
+
- Includes all dependencies for Vitessce configuration generation
|
|
15
|
+
- Use for actual visualization rendering
|
|
16
|
+
- Heavier install due to additional dependencies
|
|
17
|
+
|
|
18
|
+
Example usage (thin install):
|
|
19
|
+
>>> from portal_visualization import has_visualization
|
|
20
|
+
>>> entity = {"uuid": "abc123", "vitessce-hints": []}
|
|
21
|
+
>>> has_visualization(entity, lambda x: {})
|
|
22
|
+
False
|
|
23
|
+
>>> entity_2 = {"uuid": "def456", "vitessce-hints": ["rna", "atac"]}
|
|
24
|
+
>>> has_visualization(entity_2, lambda x: {})
|
|
25
|
+
True
|
|
26
|
+
|
|
27
|
+
Note: Check `client.py`'s get_vitessce_conf_cells_and_lifted_uuid for example usage of
|
|
28
|
+
the full install capabilities.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
# Expose lightweight functions that work with thin install
|
|
32
|
+
from .builder_factory import has_visualization, process_hints
|
|
33
|
+
|
|
34
|
+
__all__ = ["has_visualization", "process_hints"]
|
|
35
|
+
|
|
36
|
+
# pytest doctests fail without this.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
IMAGE_PYRAMID = "image_pyramid"
|
|
2
|
+
CODEX_CYTOKIT = "codex_cytokit"
|
|
3
|
+
SEQFISH = "seqFish"
|
|
4
|
+
MALDI_IMS = "MALDI-IMS"
|
|
5
|
+
CELLDIVE_DEEPCELL = "celldive_deepcell"
|
|
6
|
+
NANODESI = "NanoDESI"
|
|
7
|
+
SCRNA_SEQ_10X = "salmon_rnaseq_10x"
|
|
8
|
+
SCRNA_SEQ_SCI = "salmon_rnaseq_sciseq"
|
|
9
|
+
SCRNA_SEQ_SNARE = "salmon_rnaseq_snareseq"
|
|
10
|
+
SCRNA_SEQ_SN = "salmon_sn_rnaseq_10x"
|
|
11
|
+
SCATAC_SEQ_SCI = "sc_atac_seq_sci"
|
|
12
|
+
SCATAC_SEQ_SNARE = "sc_atac_seq_snare"
|
|
13
|
+
SCATAC_SEQ_SN = "sn_atac_seq"
|
|
14
|
+
SALMON_RNASSEQ_SLIDE = "salmon_rnaseq_slideseq"
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
from .assays import MALDI_IMS, NANODESI, SALMON_RNASSEQ_SLIDE, SEQFISH
|
|
2
|
+
from .builders.base_builders import NullViewConfBuilder
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _lazy_import_builder(builder_name):
|
|
6
|
+
"""Lazy import builder classes to avoid loading heavy dependencies.
|
|
7
|
+
|
|
8
|
+
This allows the has_visualization function to work without requiring
|
|
9
|
+
vitessce, zarr, and other heavy dependencies to be installed.
|
|
10
|
+
|
|
11
|
+
:param str builder_name: The name of the builder class to import
|
|
12
|
+
:return: The builder class
|
|
13
|
+
:rtype: type
|
|
14
|
+
|
|
15
|
+
>>> builder = _lazy_import_builder('NullViewConfBuilder')
|
|
16
|
+
>>> builder.__name__
|
|
17
|
+
'NullViewConfBuilder'
|
|
18
|
+
"""
|
|
19
|
+
if builder_name == "NullViewConfBuilder":
|
|
20
|
+
return NullViewConfBuilder
|
|
21
|
+
|
|
22
|
+
# Import all builders at once when needed
|
|
23
|
+
from .builders.anndata_builders import (
|
|
24
|
+
MultiomicAnndataZarrViewConfBuilder,
|
|
25
|
+
RNASeqAnnDataZarrViewConfBuilder,
|
|
26
|
+
SpatialMultiomicAnnDataZarrViewConfBuilder,
|
|
27
|
+
SpatialRNASeqAnnDataZarrViewConfBuilder,
|
|
28
|
+
XeniumMultiomicAnnDataZarrViewConfBuilder,
|
|
29
|
+
)
|
|
30
|
+
from .builders.imaging_builders import (
|
|
31
|
+
EpicSegImagePyramidViewConfBuilder,
|
|
32
|
+
GeoMxImagePyramidViewConfBuilder,
|
|
33
|
+
ImagePyramidViewConfBuilder,
|
|
34
|
+
IMSViewConfBuilder,
|
|
35
|
+
KaggleSegImagePyramidViewConfBuilder,
|
|
36
|
+
NanoDESIViewConfBuilder,
|
|
37
|
+
SeqFISHViewConfBuilder,
|
|
38
|
+
)
|
|
39
|
+
from .builders.object_by_analyte_builders import ObjectByAnalyteConfBuilder
|
|
40
|
+
from .builders.scatterplot_builders import (
|
|
41
|
+
ATACSeqViewConfBuilder,
|
|
42
|
+
RNASeqViewConfBuilder,
|
|
43
|
+
)
|
|
44
|
+
from .builders.sprm_builders import (
|
|
45
|
+
MultiImageSPRMAnndataViewConfBuilder,
|
|
46
|
+
StitchedCytokitSPRMViewConfBuilder,
|
|
47
|
+
TiledSPRMViewConfBuilder,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Map builder names to classes
|
|
51
|
+
builders = {
|
|
52
|
+
"MultiomicAnndataZarrViewConfBuilder": MultiomicAnndataZarrViewConfBuilder,
|
|
53
|
+
"RNASeqAnnDataZarrViewConfBuilder": RNASeqAnnDataZarrViewConfBuilder,
|
|
54
|
+
"SpatialMultiomicAnnDataZarrViewConfBuilder": SpatialMultiomicAnnDataZarrViewConfBuilder,
|
|
55
|
+
"SpatialRNASeqAnnDataZarrViewConfBuilder": SpatialRNASeqAnnDataZarrViewConfBuilder,
|
|
56
|
+
"XeniumMultiomicAnnDataZarrViewConfBuilder": XeniumMultiomicAnnDataZarrViewConfBuilder,
|
|
57
|
+
"EpicSegImagePyramidViewConfBuilder": EpicSegImagePyramidViewConfBuilder,
|
|
58
|
+
"GeoMxImagePyramidViewConfBuilder": GeoMxImagePyramidViewConfBuilder,
|
|
59
|
+
"ImagePyramidViewConfBuilder": ImagePyramidViewConfBuilder,
|
|
60
|
+
"IMSViewConfBuilder": IMSViewConfBuilder,
|
|
61
|
+
"KaggleSegImagePyramidViewConfBuilder": KaggleSegImagePyramidViewConfBuilder,
|
|
62
|
+
"NanoDESIViewConfBuilder": NanoDESIViewConfBuilder,
|
|
63
|
+
"SeqFISHViewConfBuilder": SeqFISHViewConfBuilder,
|
|
64
|
+
"ObjectByAnalyteConfBuilder": ObjectByAnalyteConfBuilder,
|
|
65
|
+
"ATACSeqViewConfBuilder": ATACSeqViewConfBuilder,
|
|
66
|
+
"RNASeqViewConfBuilder": RNASeqViewConfBuilder,
|
|
67
|
+
"MultiImageSPRMAnndataViewConfBuilder": MultiImageSPRMAnndataViewConfBuilder,
|
|
68
|
+
"StitchedCytokitSPRMViewConfBuilder": StitchedCytokitSPRMViewConfBuilder,
|
|
69
|
+
"TiledSPRMViewConfBuilder": TiledSPRMViewConfBuilder,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if builder_name in builders:
|
|
73
|
+
return builders[builder_name]
|
|
74
|
+
else: # pragma: no cover
|
|
75
|
+
raise ValueError(f"Unknown builder: {builder_name}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# This function processes the hints and returns a tuple of booleans
|
|
79
|
+
# indicating which builder to use for the given entity.
|
|
80
|
+
def process_hints(hints):
|
|
81
|
+
hints = set(hints)
|
|
82
|
+
is_image = "is_image" in hints
|
|
83
|
+
is_rna = "rna" in hints
|
|
84
|
+
is_atac = "atac" in hints
|
|
85
|
+
is_sprm = "sprm" in hints
|
|
86
|
+
is_codex = "codex" in hints
|
|
87
|
+
is_anndata = "anndata" in hints
|
|
88
|
+
is_json = "json_based" in hints
|
|
89
|
+
is_spatial = "spatial" in hints
|
|
90
|
+
is_support = "is_support" in hints
|
|
91
|
+
is_seg_mask = "segmentation_mask" in hints
|
|
92
|
+
is_geomx = "geomx" in hints
|
|
93
|
+
is_xenium = "xenium" in hints
|
|
94
|
+
is_epic = "epic" in hints
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
is_image,
|
|
98
|
+
is_rna,
|
|
99
|
+
is_atac,
|
|
100
|
+
is_sprm,
|
|
101
|
+
is_codex,
|
|
102
|
+
is_anndata,
|
|
103
|
+
is_json,
|
|
104
|
+
is_spatial,
|
|
105
|
+
is_support,
|
|
106
|
+
is_seg_mask,
|
|
107
|
+
is_geomx,
|
|
108
|
+
is_xenium,
|
|
109
|
+
is_epic,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# This function is the main entrypoint for the builder factory.
|
|
114
|
+
# It returns the correct builder for the given entity.
|
|
115
|
+
#
|
|
116
|
+
# The entity is a dict that contains the entity UUID and metadata.
|
|
117
|
+
def get_view_config_builder(entity, get_entity, parent=None, epic_uuid=None):
|
|
118
|
+
"""Get the appropriate builder class for an entity.
|
|
119
|
+
|
|
120
|
+
Returns a builder class (not an instance) that can be used to generate
|
|
121
|
+
Vitessce configurations for the given entity.
|
|
122
|
+
|
|
123
|
+
:param dict entity: Entity response from search index
|
|
124
|
+
:param callable get_entity: Function to retrieve entity by UUID
|
|
125
|
+
:param str parent: Parent entity UUID if this is a support dataset
|
|
126
|
+
:param str epic_uuid: EPIC UUID if this is an EPIC-related dataset
|
|
127
|
+
:return: Builder class
|
|
128
|
+
:rtype: type
|
|
129
|
+
"""
|
|
130
|
+
builder_name = _get_builder_name(entity, get_entity, parent, epic_uuid)
|
|
131
|
+
return _lazy_import_builder(builder_name)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _get_builder_name(entity, get_entity, parent=None, epic_uuid=None):
|
|
135
|
+
"""Get the name of the appropriate builder for an entity.
|
|
136
|
+
|
|
137
|
+
This is the core decision logic that doesn't require importing heavy dependencies.
|
|
138
|
+
Returns the builder class name as a string.
|
|
139
|
+
|
|
140
|
+
:param dict entity: Entity response from search index
|
|
141
|
+
:param callable get_entity: Function to retrieve entity by UUID
|
|
142
|
+
:param str parent: Parent entity UUID if this is a support dataset
|
|
143
|
+
:param str epic_uuid: EPIC UUID if this is an EPIC-related dataset
|
|
144
|
+
:return: Builder class name
|
|
145
|
+
:rtype: str
|
|
146
|
+
"""
|
|
147
|
+
if entity.get("uuid") is None:
|
|
148
|
+
raise ValueError("Provided entity does not have a uuid")
|
|
149
|
+
assay_name = entity.get("soft_assaytype")
|
|
150
|
+
hints = entity.get("vitessce-hints", [])
|
|
151
|
+
(
|
|
152
|
+
is_image,
|
|
153
|
+
is_rna,
|
|
154
|
+
is_atac,
|
|
155
|
+
is_sprm,
|
|
156
|
+
is_codex,
|
|
157
|
+
is_anndata,
|
|
158
|
+
is_json,
|
|
159
|
+
is_spatial,
|
|
160
|
+
is_support,
|
|
161
|
+
is_seg_mask,
|
|
162
|
+
is_geomx,
|
|
163
|
+
is_xenium,
|
|
164
|
+
is_epic,
|
|
165
|
+
) = process_hints(hints)
|
|
166
|
+
|
|
167
|
+
# 'epic" is the only hint for object x analyte EPICs
|
|
168
|
+
if is_epic and len(hints) == 1:
|
|
169
|
+
return "ObjectByAnalyteConfBuilder"
|
|
170
|
+
|
|
171
|
+
# vis-lifted image pyramids
|
|
172
|
+
if parent is not None:
|
|
173
|
+
# TODO: For now epic (base image's) support datasets doesn't have any hints
|
|
174
|
+
if is_seg_mask and epic_uuid:
|
|
175
|
+
return "EpicSegImagePyramidViewConfBuilder"
|
|
176
|
+
elif is_seg_mask:
|
|
177
|
+
return "KaggleSegImagePyramidViewConfBuilder"
|
|
178
|
+
|
|
179
|
+
elif is_support and is_image:
|
|
180
|
+
ancestor_assaytype = get_entity(parent).get("soft_assaytype")
|
|
181
|
+
if ancestor_assaytype == SEQFISH:
|
|
182
|
+
# e.g. parent = c6a254b2dc2ed46b002500ade163a7cc
|
|
183
|
+
# e.g. support = 9db61adfc017670a196ea9b3ca1852a0
|
|
184
|
+
return "SeqFISHViewConfBuilder"
|
|
185
|
+
elif ancestor_assaytype == MALDI_IMS:
|
|
186
|
+
# e.g. parent = 3bc3ad124014a632d558255626bf38c9
|
|
187
|
+
# e.g. support = a6116772446f6d1c1f6b3d2e9735cfe0
|
|
188
|
+
return "IMSViewConfBuilder"
|
|
189
|
+
elif ancestor_assaytype == NANODESI:
|
|
190
|
+
# e.g. parent = 6b93107731199733f266bbd0f3bc9747
|
|
191
|
+
# e.g. support = e1c4370da5523ab5c9be581d1d76ca20
|
|
192
|
+
return "NanoDESIViewConfBuilder"
|
|
193
|
+
else:
|
|
194
|
+
# e.g. parent = 8adc3c31ca84ec4b958ed20a7c4f4919
|
|
195
|
+
# e.g. support = f9ae931b8b49252f150d7f8bf1d2d13f
|
|
196
|
+
return "ImagePyramidViewConfBuilder"
|
|
197
|
+
else:
|
|
198
|
+
return "NullViewConfBuilder"
|
|
199
|
+
|
|
200
|
+
if is_image:
|
|
201
|
+
if is_rna:
|
|
202
|
+
# e.g. Visium (no probes) [Salmon + Scanpy]
|
|
203
|
+
# sample entity (on dev): 72ec02cf1390428c1e9dc2c88928f5f5
|
|
204
|
+
return "SpatialMultiomicAnnDataZarrViewConfBuilder"
|
|
205
|
+
if is_sprm and is_anndata:
|
|
206
|
+
# e.g. CellDIVE [DeepCell + SPRM]
|
|
207
|
+
# sample entity: c3be5650e93907b68ddbdb22b948db32
|
|
208
|
+
return "MultiImageSPRMAnndataViewConfBuilder"
|
|
209
|
+
if is_codex:
|
|
210
|
+
if is_json:
|
|
211
|
+
# legacy JSON-based dataset, e.g. b69d1e2ad1bf1455eee991fce301b191
|
|
212
|
+
return "TiledSPRMViewConfBuilder"
|
|
213
|
+
# e.g. CODEX [Cytokit + SPRM]
|
|
214
|
+
# sample entity: 43213991a54ce196d406707ffe2e86bd
|
|
215
|
+
return "StitchedCytokitSPRMViewConfBuilder"
|
|
216
|
+
if is_geomx:
|
|
217
|
+
return "GeoMxImagePyramidViewConfBuilder"
|
|
218
|
+
if is_xenium:
|
|
219
|
+
return "XeniumMultiomicAnnDataZarrViewConfBuilder"
|
|
220
|
+
if is_rna:
|
|
221
|
+
# multiomic mudata, e.g. 10x Multiome, SNARE-Seq, etc.
|
|
222
|
+
# e.g. 272789a950b2b5d4b9387a1cf66ad487 on dev
|
|
223
|
+
if is_atac:
|
|
224
|
+
return "MultiomicAnndataZarrViewConfBuilder"
|
|
225
|
+
if is_json:
|
|
226
|
+
# e.g. c019a1cd35aab4d2b4a6ff221e92aaab
|
|
227
|
+
return "RNASeqViewConfBuilder"
|
|
228
|
+
# if not JSON, assume that the entity is AnnData-backed
|
|
229
|
+
# TODO - once "anndata" hint is added to the hints for this assay, use that instead
|
|
230
|
+
if assay_name == SALMON_RNASSEQ_SLIDE:
|
|
231
|
+
# e.g. 2a590db3d7ab1e1512816b165d95cdcf
|
|
232
|
+
return "SpatialRNASeqAnnDataZarrViewConfBuilder"
|
|
233
|
+
# e.g. e65175561b4b17da5352e3837aa0e497
|
|
234
|
+
return "RNASeqAnnDataZarrViewConfBuilder"
|
|
235
|
+
if is_atac:
|
|
236
|
+
# e.g. d4493657cde29702c5ed73932da5317c
|
|
237
|
+
return "ATACSeqViewConfBuilder"
|
|
238
|
+
|
|
239
|
+
# any entity with no hints, e.g. 2c2179ea741d3bbb47772172a316a2bf
|
|
240
|
+
return "NullViewConfBuilder"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def has_visualization(entity, get_entity, parent=None, epic_uuid=None):
|
|
244
|
+
"""Check if an entity has a visualization without loading heavy dependencies.
|
|
245
|
+
|
|
246
|
+
This function works with the thin install (no [full] extras required).
|
|
247
|
+
|
|
248
|
+
:param dict entity: Entity response from search index
|
|
249
|
+
:param callable get_entity: Function to retrieve entity by UUID
|
|
250
|
+
:param str parent: Parent entity UUID if this is a support dataset
|
|
251
|
+
:param str epic_uuid: EPIC UUID if this is an EPIC-related dataset
|
|
252
|
+
:return: True if the entity has a visualization, False otherwise
|
|
253
|
+
:rtype: bool
|
|
254
|
+
"""
|
|
255
|
+
builder_name = _get_builder_name(entity, get_entity, parent, epic_uuid)
|
|
256
|
+
return builder_name != "NullViewConfBuilder"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# pytest doctests fail without this.
|