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.
@@ -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.