Skip to content

Commit

Permalink
Fixed linting and test issues, updated Readme
Browse files Browse the repository at this point in the history
  • Loading branch information
tkakar committed Sep 13, 2024
1 parent 52293d3 commit 76e9552
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 59 deletions.
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@ $ pip install .
$ src/vis-preview.py --help
usage: vis-preview.py [-h] (--url URL | --json JSON) [--assaytypes_url URL]
[--assets_url URL] [--token TOKEN] [--marker MARKER]
[--to_json]
[--to_json] [--epic_uuid UUID]
Given HuBMAP Dataset JSON, generate a Vitessce viewconf, and load vitessce.io.
required arguments (one of the following):
--url URL URL which returns Dataset JSON
--json JSON File containing Dataset JSON
optional arguments:
-h, --help show this help message and exit
--url URL URL which returns Dataset JSON
--json JSON File containing Dataset JSON
--assaytypes_url URL AssayType service; default:
https://ingest.api.hubmapconsortium.org/assaytype/
--assets_url URL Assets endpoint; default:
Expand All @@ -40,12 +39,25 @@ optional arguments:
--marker MARKER Marker to highlight in visualization; Only used in
some visualizations.
--to_json Output viewconf, rather than open in browser.
--epic_uuid uuid for EPIC dataset, if available.
--epic_uuid UUID uuid of the EPIC dataset
```


```
Note: To get the token, look for Authorization Bearer {token represented by a long string} under `search-api` network calls under th network tab in developer's tool when browsing a dataset in portal
Notes:
1. To get the token, look for Authorization Bearer {token represented by a long string} under `search-api` network calls under th network tab in developer's tool when browsing a dataset in portal
2. If you added an argument to the vis-preview.py script, do update the help docs in README, otherwise build will throw error
3.
```



## Build & Testing
```
To build: `python -m build`
`To run the tests `./test.sh`. Install the `flake8` and `autopep8` packages.
```

## Background
Expand Down
6 changes: 3 additions & 3 deletions src/portal_visualization/builder_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ def process_hints(hints):
# `get_assaytype` is a function which takes an entity UUID and returns
# a dict containing the assaytype and vitessce-hints for that entity.
def get_view_config_builder(entity, get_assaytype, parent=None):
uuid = entity.get("uuid")
if uuid is None:
if entity.get("uuid") is None:
raise ValueError("Provided entity does not have a uuid")
assay = get_assaytype(uuid)
assay = get_assaytype(entity)
assay_name = assay.get("assaytype")
hints = assay.get("vitessce-hints", [])
(
Expand Down Expand Up @@ -133,6 +132,7 @@ def get_view_config_builder(entity, get_assaytype, parent=None):
# any entity with no hints, e.g. 2c2179ea741d3bbb47772172a316a2bf
return NullViewConfBuilder


def has_visualization(entity, get_assaytype, parent=None):
builder = get_view_config_builder(entity, get_assaytype, parent)
return builder != NullViewConfBuilder
45 changes: 25 additions & 20 deletions src/portal_visualization/builders/epic_builders.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import abstractmethod
from vitessce import VitessceConfig, ObsSegmentationsOmeTiffWrapper, AnnDataWrapper, get_initial_coordination_scope_prefix, CoordinationLevel as CL
from vitessce import VitessceConfig, ObsSegmentationsOmeTiffWrapper, AnnDataWrapper, \
get_initial_coordination_scope_prefix, CoordinationLevel as CL
from .base_builders import ConfCells
from ..utils import get_conf_cells
from .base_builders import ViewConfBuilder
Expand All @@ -9,12 +10,14 @@

# EPIC builders take in a vitessce conf output by a previous builder and modify it
# accordingly to add the EPIC-specific configuration.


class EPICConfBuilder(ViewConfBuilder):
def __init__(self, base_conf: ConfCells, epic_uuid, entity, groups_token, assets_endpoint, **kwargs) -> None:
super().__init__(entity, groups_token, assets_endpoint, **kwargs)

conf, cells = base_conf

if conf is None:
raise ValueError("ConfCells object must have a conf attribute")

Expand Down Expand Up @@ -50,7 +53,7 @@ def _apply(self, conf): # pragma: no cover
def zarr_store_url(self):
adata_url = self._build_assets_url(zarr_path, use_token=False)
return adata_url

def segmentations_url(self):
seg_url = self._build_assets_url('', use_token=False)
return seg_url
Expand All @@ -62,31 +65,36 @@ def _apply(self, conf):
datasets = conf.get_datasets()
# TODO: add the correct path to the segmentation mask ome-tiff (image-pyramid)?
seg_path = f'{self.segmentations_url}/'
seg_path = 'https://assets.hubmapconsortium.org/c9d9ab5c9ee9642b60dd351024968627/ometiff-pyramids/VAN0042-RK-3-18-registered-PAS-to-postAF-registered.ome_mask.ome.tif?token=AgzQXm7nvOW32vWw0EPpKonwbOqjNBzNvvW1p15855NoYglJxyfkC8rlJJWy8V6E8MeyXOwlpKdNBnHb5qnv7f8oeeG',
seg_path = (
'https://assets.hubmapconsortium.org/c9d9ab5c9ee9642b60dd351024968627/'
'ometiff-pyramids/VAN0042-RK-3-18-registered-PAS-to-postAF-registered.ome_mask.ome.tif?'
'token=AgzQXm7nvOW32vWw0EPpKonwbOqjNBzNvvW1p15855NoYglJxyfkC8rlJJWy8V6E8MeyXOwlpKdNBnHb5qnv7f8oeeG'
)
mask_names = self.read_metadata_from_url()
mask_names = ['mask1', 'mask2'] #for testing purposes
if(mask_names is not None):
mask_names = ['mask1', 'mask2'] # for testing purposes
if (mask_names is not None):
segmentation_objects = create_segmentation_objects(zarr_url, mask_names)
segmentations = ObsSegmentationsOmeTiffWrapper (
segmentations = ObsSegmentationsOmeTiffWrapper(
img_url=seg_path,
obs_types_from_channel_names=True,
coordination_values = {
coordination_values={
"fileUid": "segmentation-mask"
}
)
)

for dataset in datasets:
for dataset in datasets:
dataset.add_object(segmentations)
for obj in segmentation_objects:
dataset.add_object(obj)

# TODO: what happens if these views already exist , and if there are other views, how to place these?
spatial_view = conf.add_view("spatialBeta", dataset=dataset, w=8, h=12)
lc_view = conf.add_view("layerControllerBeta", dataset=dataset, w=4, h=12, x=8, y=0)
# without add_view can't access the metaCoordincatinSpace
# (e.g. get_coordination_scope() https://python-docs.vitessce.io/api_config.html?highlight=coordination#vitessce.config.VitessceChainableConfig.get_coordination_scope)
# without add_view can't access the metaCoordincatinSpace
# (e.g. get_coordination_scope() https://python-docs.vitessce.io/api_config.html?
# highlight=coordination#vitessce.config.VitessceChainableConfig.get_coordination_scope)
conf.link_views_by_dict([spatial_view, lc_view], {
"segmentationLayer":CL([
"segmentationLayer": CL([
{
"fileUid": "segmentation-mask",
"spatialLayerVisible": True,
Expand All @@ -96,7 +104,6 @@ def _apply(self, conf):

}, meta=True, scope_prefix=get_initial_coordination_scope_prefix("A", "obsSegmentations"))


def read_metadata_from_url(self):
url = f'{self.zarr_store_url()}/metadata.json'
print(f"metadata.json URL: {url}")
Expand All @@ -114,23 +121,21 @@ def read_metadata_from_url(self):
return None
else:
# raise Exception(f"Failed to retrieve data: {response.status_code} - {response.reason}")
pass # for testing purposes
pass # for testing purposes


def create_segmentation_objects(base_url, mask_names):
segmentation_objects = []
for mask_name in mask_names:
mask_url = f'{base_url}/{mask_name}.zarr',
segmentations_zarr= AnnDataWrapper(
segmentations_zarr = AnnDataWrapper(
adata_url=mask_url,
obs_locations_path="obsm/X_spatial",
obs_labels_names= mask_name,
obs_labels_names=mask_name,
coordination_values={
"obsType": mask_name
}
)
segmentation_objects.append(segmentations_zarr)

return segmentation_objects


34 changes: 16 additions & 18 deletions src/vis-preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from portal_visualization.builder_factory import get_view_config_builder
from portal_visualization.epic_factory import get_epic_builder


def main(): # pragma: no cover
defaults = json.load((Path(__file__).parent / 'defaults.json').open())
assets_default_url = defaults['assets_url']
Expand Down Expand Up @@ -42,17 +43,16 @@ def main(): # pragma: no cover
parser.add_argument(
'--to_json', action='store_true',
help='Output viewconf, rather than open in browser.')

# parser.add_argument(
# '--parent_uuid', action='store_true',
# help='Parent uuid for the dataset',
# default=None)
parser.add_argument(
'--epic_uuid', metavar='URL',
help='Epic dataset"s uuid',
'--epic_uuid', metavar='UUID',
help='uuid of the EPIC dataset',
default=None)
#

#
# parser.add_argument(
# '--epic_builder', action='store_true',
# help='Whether to use the epic_builder or not',
Expand All @@ -74,7 +74,8 @@ def main(): # pragma: no cover
json_str = args.json.read_text()
entity = json.loads(json_str)

def get_assaytype(uuid):
def get_assaytype(entity):
uuid = entity.get("uuid")
headers = {}
if args.token:
headers['Authorization'] = f'Bearer {args.token}'
Expand All @@ -87,39 +88,36 @@ def get_assaytype(uuid):
data = response.json()
return data
except Exception as e:
print("Error in parsing the response {str(e)}")
print(f"Error in parsing the response {str(e)}")
except Exception as e:
print(f"Error accessing {defaults['assaytypes_url']}{uuid}: {str(e)}")



Builder = get_view_config_builder(entity, get_assaytype)
builder = Builder(entity, args.token, args.assets_url)
print(f'Using: {builder.__class__.__name__}', file=stderr)
conf_cells = builder.get_conf_cells(marker=marker)
if(epic_uuid is not None and conf_cells is not None):
EpicBuilder = get_epic_builder(conf_cells, epic_uuid)

if (epic_uuid is not None and conf_cells is not None):
EpicBuilder = get_epic_builder(conf_cells, epic_uuid)
epic_builder = EpicBuilder(conf_cells, epic_uuid, entity, args.token, args.assets_url)
print(f'Using: {epic_builder.__class__.__name__}', file=stderr)
conf_cells = epic_builder.get_conf_cells()


if isinstance(conf_cells.conf, list):
conf_as_json = json.dumps(conf_cells.conf[0])
else:
conf_as_json = json.dumps(conf_cells.conf)

if args.to_json:
print(conf_as_json)
## For testing

# For testing
# with open ('epic.json','w') as file:
# if isinstance(conf_cells.conf, list):
# json.dump( conf_cells.conf[0], file, indent=4, separators=(',', ': '))
# else:
# json.dump( conf_cells.conf, file, indent=4, separators=(',', ': '))



data_url = f'data:,{quote_plus(conf_as_json)}'
vitessce_url = f'http://vitessce.io/#?url={data_url}'
open_new_tab(vitessce_url)
Expand Down
3 changes: 2 additions & 1 deletion test/test_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def get_assaytype(entity):
)
def test_has_visualization(has_vis_entity):
has_vis, entity = has_vis_entity
print(has_vis, entity)
parent = entity.get("parent") or None # Only used for image pyramids
assert has_vis == has_visualization(entity, get_assaytype, parent)

Expand Down Expand Up @@ -173,7 +174,7 @@ def test_entity_to_vitessce_conf(entity_path, mocker):
# TODO: This is a stub for now, real tests for the EPIC builders
# will be added in a future PR.

epic_builder = get_epic_builder(entity["uuid"])
epic_builder = get_epic_builder(conf, entity["uuid"])
assert epic_builder is not None

if conf is None:
Expand Down
22 changes: 11 additions & 11 deletions test/test_epic_builders.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import pytest
from src.portal_visualization.epic_factory import get_epic_builder
# import pytest
# from src.portal_visualization.epic_factory import get_epic_builder
# from src.portal_visualization.builders.base_builders import ConfCells


@pytest.mark.parametrize(
"epic_uuid, expected",
[
("epic_uuid", "SegmentationMaskBuilder"),
],
)
def test_get_epic_builder(epic_uuid, expected):
assert get_epic_builder(epic_uuid).__name__ == expected
# @pytest.mark.parametrize(
# "parent_conf, epic_uuid, expected",
# [
# ("epic_uuid", "SegmentationMaskBuilder"),
# ],
# )
# def test_get_epic_builder(parent_conf, epic_uuid, expected):
# assert get_epic_builder(parent_conf, epic_uuid).__name__ == expected

0 comments on commit 76e9552

Please sign in to comment.