-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
3,289 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
|
||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2023 LabShare | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# JupyterLab Polus Render | ||
JupyterLab Polus Render makes Polus Render available as a JupyterLab extension. | ||
|
||
Polus Render allows visualizing tiled raster datasets in Zarr and TIFF formats, as well as vector overlays in MicroJSON format. It uses lookup tables to map intensity values in these datasets to colors. | ||
|
||
The are three ways to load the data: | ||
1. Specifying a URL to a server serving the data. | ||
2. Specifying a local path to a file from JupyterLab. | ||
3. Dragging-and-dropping the dataset does not use a server. | ||
</br> | ||
|
||
Please note that usage differs significantly from https://pypi.org/project/polus-render/0.0.4.0.1.5/ | ||
|
||
<img src="images/home.png"/> | ||
|
||
# Requirements | ||
* Python 3.9+ | ||
|
||
# Installation | ||
``` | ||
pip install "git+https://github.com/PolusAI/jupyterlab-extensions.git#egg=jupyterlab_polus_render&subdirectory=jupyterlab_polus_render" | ||
``` | ||
You will need to restart Jupyter Server for `render-server-ext` endpoints to take effect. | ||
|
||
# Project File Structure | ||
``` | ||
jupyterlab_polus_render | ||
| LICENSE | ||
| MANIFEST.in // Packaging entries | ||
| pyproject.toml // Pypi config | ||
| README | ||
└───render-server-ext // Server extension used by jupyterlab_polus_render | ||
└───polus | ||
| polus_render.py // Main file, contains render function used by user | ||
``` | ||
|
||
# Build Instructions | ||
- cd to `jupyterlab_polus_render` root directory. | ||
- `py -m build` | ||
- `py -m twine upload dist/*` | ||
- Enter `__token__` as user and reference API keys for password | ||
|
||
## NOTE: | ||
- For each upload, version number must be changed in `pyproject.toml` | ||
- Add additional files to `MANIFEST.in` to bundle them with Pypi package | ||
|
||
# Render: Static build functionality | ||
JupyterLab Polus Render is bundled with a build of Polus Render which supporting the following functionality | ||
| Version | Zarr from URL/Path | TIF from URL/Path | Micro-JSON Support | Zarr/TIF Drag & Drop | Micro-JSON Drag & Drop | | ||
|----------------|---------------|---------------|----------------|-----------|-----| | ||
| Static | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | ||
|
||
# Drag & Drop Demo | ||
<img src="images/drag-drop.gif"/> | ||
|
||
# Sample usage | ||
``` Python | ||
from polus.polus_render import render | ||
|
||
# pathlib and urllib are built-ins | ||
from urllib.parse import urlparse | ||
from pathlib import Path | ||
|
||
# Make sure to keep track of your JupyterLab url and file root if your root is not at "/home/joyvan". | ||
JL_URL = urlparse("https://<JUPYTERHUB_URL>/user/<USERNAME>/user-namespaces/lab?") | ||
|
||
# Embeds an IFrame of a static build of Polus Render into Jupyter Lab, this is sufficient if your file root is "/home/joyvan/" | ||
render(nbhub_url=JL_URL) | ||
|
||
# Same as above; however, if your file root is "/Users/jeff.chen/", your invocation will require nb_root argument | ||
render(nbhub_url=JL_URL, \ | ||
nb_root=Path("/Users/jeff.chen/")) | ||
|
||
# Embeds an IFrame of a static build of Polus Render with an image file hosted at "https://viv-demo.storage.googleapis.com/LuCa-7color_Scan1/" | ||
render(nbhub_url=JL_URL, \ | ||
image_location=urlparse("https://viv-demo.storage.googleapis.com/LuCa-7color_Scan1/")) | ||
|
||
# Embeds an IFrame of a static build of Polus Render with an image hosted at "/home/joyvan/zarr files/pyramid.zarr" | ||
render(nbhub_url=JL_URL, \ | ||
image_location=Path(r"zarr files/pyramid.zarr")) | ||
|
||
# Embeds an IFrame of a static build of Polus Render with an image and overlay file | ||
render(nbhub_url=JL_URL, \ | ||
image_location=Path("zarr files/pyramid.zarr"), \ | ||
microjson_overlay_location=Path("overlay files/x00_y01_c1_segmentations.json")) | ||
|
||
# Embeds an IFrame of a static build of Polus Render with an image and overlay file served online | ||
render(nbhub_url=JL_URL, \ | ||
image_location=urlparse("https://files.scb-ncats.io/pyramids/segmentations/x00_y01_c1.ome.tif"), \ | ||
microjson_overlay_location=urlparse("https://files.scb-ncats.io/pyramids/segmentations/x00_y03_c1_segmentations.json")) | ||
``` | ||
|
||
# Functions | ||
``` Python | ||
def render(nbhub_url:ParseResult, nb_root:PurePath = Path("/home/jovyan/"), image_location:Union[ParseResult, PurePath] = "", microjson_overlay_location:Union[ParseResult, PurePath] = "", width:int=960, height:int=500)->str: | ||
""" | ||
Embeds a static build of render into a JupyterLabs notebook with the help of `render-server-ext` | ||
Param: | ||
nbhub_url (ParseResult): URL used used for jupyterhub. Contains '/lab/' in its uri | ||
nb_root (ParseResult): Root path used to search files in. Default is '/home/jovyan/' which works for notebooks hub. Can be set to empty path | ||
if absolute paths will be used for images and json files. | ||
image_location(ParseResult|Purepath): Acquired from urllib.parse.ParseResult or Path, renders url in render. | ||
If not specified, renders default render url. | ||
microjson_overlay_location(ParseResult|Purepath): Acquired from urllib.parse.ParseResult or Path, renders url in render. | ||
If not specified, renders default render url | ||
width (int): width of render to be displayed, default is 960 | ||
height (int): height of render to be displayed, default is 500 | ||
Returns: Render URL | ||
""" | ||
``` | ||
|
||
# Implementation Details | ||
- Render application is loaded in an IFrame. | ||
- render() builds up URL scheme fragments for render url, image url, and microjson url. It then combines url fragments into a single url which is displayed through an embedded IFrame. | ||
- Static build of Polus Render as well as files to be displayed are served by Jupyter Server extension | ||
- Dragging-and-dropping the dataset does not use a server. It calls an API from the front end (It should the this under the hood https://developer.mozilla.org/en-US/docs/Web/API/File_API). |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import os | ||
from IPython.display import display, IFrame | ||
from urllib.parse import ParseResult | ||
from pathlib import PurePath, Path | ||
from typing import Union | ||
|
||
|
||
|
||
def render(nbhub_url:ParseResult, nb_root:PurePath = Path("/home/jovyan/"), image_location:Union[ParseResult, PurePath] = "", microjson_overlay_location:Union[ParseResult, PurePath] = "", width:int=960, height:int=500)->str: | ||
""" | ||
Embeds a static build of render into a JupyterLabs notebook with the help of `render-server-ext` | ||
Param: | ||
nbhub_url (ParseResult): URL used used for jupyterhub. Contains '/lab/' in its uri | ||
nb_root (ParseResult): Root path used to search files in. Default is '/home/jovyan/' which works for notebooks hub. Can be set to empty path | ||
if absolute paths will be used for images and json files. | ||
image_location(ParseResult|Purepath): Acquired from urllib.parse.ParseResult or Path, renders url in render. | ||
If not specified, renders default render url. | ||
microjson_overlay_location(ParseResult|Purepath): Acquired from urllib.parse.ParseResult or Path, renders url in render. | ||
If not specified, renders default render url | ||
width (int): width of render to be displayed, default is 960 | ||
height (int): height of render to be displayed, default is 500 | ||
Returns: Render URL | ||
""" | ||
assert(nbhub_url) | ||
base_nbhub = nbhub_url.geturl().rpartition("lab")[0] | ||
# Extract url from file path if provided. ?imageUrl is required scheme for render | ||
if isinstance(image_location, PurePath): | ||
image_location = "?imageUrl=" + base_nbhub + "render/file/" + os.path.join(str(nb_root), str(image_location)) | ||
|
||
# Otherwise, extract url from user provided url if provided | ||
elif isinstance(image_location, ParseResult): | ||
image_location = "?imageUrl=" + image_location.geturl() # Can be manually rebuilt to check if a valid format url is sent | ||
|
||
# Do the same but for JSON | ||
if isinstance(microjson_overlay_location, PurePath): | ||
microjson_overlay_location = "&overlayUrl=" + base_nbhub + "render/file/" + os.path.join(str(nb_root), str(microjson_overlay_location)) | ||
|
||
elif isinstance(microjson_overlay_location, ParseResult): | ||
microjson_overlay_location = "&overlayUrl=" + microjson_overlay_location.geturl() | ||
|
||
# static render | ||
render_url = f"{base_nbhub}static/render/render-ui/index.html" | ||
|
||
# Display render | ||
display(IFrame(src=(f"{render_url}{image_location}{microjson_overlay_location}") | ||
, width=width, height=height)) | ||
|
||
return f"{render_url}{image_location}{microjson_overlay_location}" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[tool.poetry] | ||
name = "jupyterlab_polus_render" | ||
version = "0.1.0" | ||
description = "Displays Polus Render dashboard in Jupyter Notebooks." | ||
authors = ["Jeff Chen <[email protected]>"] | ||
packages = [{ include = "polus"}, {include = "render-server-ext"}] | ||
License = "MIT" | ||
readme = "README.md" | ||
repository = "https://github.com/PolusAI/jupyterlab-extensions" | ||
|
||
[tool.poetry.dependencies] | ||
python = ">=3.9" | ||
render-server-ext = { path = "./render-server-ext/", develop = false } | ||
|
||
|
||
[build-system] | ||
requires = ["poetry-core>=1.0.0"] | ||
build-backend = "poetry.core.masonry.api" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2023 LabShare | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Render Server Ext | ||
Jupyter Server Extension serves files and provides a static build of Polus Render. | ||
|
||
<img src="images/home.png"/> | ||
|
||
# Requirements | ||
- Jupyter Server 2.7.0 | ||
|
||
# Adding a static build of Polus Render | ||
- Remove all existing files in `~/render-server-ext/static/render-ui/`. | ||
- Run `npx nx build render-ui` in the root of your Polus Render folder | ||
- Transfer generated files from `~/Polus Render/dist/apps/render-ui/` into `~/render-server-ext/static/render-ui/`. | ||
|
||
# API Endpoints | ||
- `/render/default/(.*)`: Help on usage of extension | ||
- `/static/render/render-ui/index.html`: Serves static build of Polus Render. | ||
- `/render/file/(.+)`: Serves files at a specfied path. Does not serve directories. | ||
|
||
# Examples | ||
For the following examples, JupyterLab is ran locally at `https://localhost:7832/lab` | ||
|
||
|
||
## Render-UI | ||
URL: `http://localhost:7832/static/render/render-ui/index.html?imageUrl=http://localhost:7832/render/file//home/jovyan/work/images/4by4large_intensity_image.ome.tif` | ||
|
||
<img src="images/renderui-1.png"/> | ||
|
||
|
||
URL: `http://localhost:7832/static/render/render-ui/index.html?imageUrl=http://localhost:7832/render/file//home/jovyan/work/images/x00_y01_p02_c1.ome.tif&overlayUrl=http://localhost:7832/render/file//home/jovyan/work/overlays/combined.json` | ||
|
||
<img src="images/renderui-2.png"/> | ||
|
||
|
||
## Image | ||
URL: `http://localhost:7832/render/file/home/jovyan/work/samples/butterfly.jpeg` | ||
|
||
<img src="images/image.png"/> | ||
|
||
## Markdown | ||
URL: `http://localhost:7832/render/file/home/jovyan/work/samples/README.md` | ||
|
||
<img src="images/markdown.png"/> | ||
|
8 changes: 8 additions & 0 deletions
8
jupyterlab_polus_render/render-server-ext/etc/jupyter/jupyter_server_config.d/serve.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"ServerApp": { | ||
"jpserver_extensions": { | ||
"serve": true | ||
} | ||
} | ||
} | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions
8
jupyterlab_polus_render/render-server-ext/jupyter_server_config.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
"""Configuration file for jupyter-server extensions.""" | ||
# ------------------------------------------------------------------------------ | ||
# Application(SingletonConfigurable) configuration | ||
# ------------------------------------------------------------------------------ | ||
# The date format used by logging formatters for %(asctime)s | ||
c.Application.log_datefmt = ( # type:ignore[name-defined] | ||
"%Y-%m-%d %H:%M:%S Render-Server-Ext" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
[build-system] | ||
requires = ["hatchling"] | ||
build-backend = "hatchling.build" | ||
|
||
[project] | ||
name = "render-server-ext" | ||
description = "Serves files and serves a local build of Polus Render" | ||
readme = "README.md" | ||
license = "MIT" | ||
requires-python = ">=3.9" | ||
dependencies = [ | ||
"jinja2", | ||
] | ||
authors = [{name = "Jeff Chen", email="[email protected]"}] | ||
version = "0.1.0" | ||
|
||
[project.urls] | ||
Homepage = "https://github.com/PolusAI/jupyterlab-extensions" | ||
|
||
[project.scripts] | ||
jupyter-server = "serve.application:main" | ||
|
||
[tool.hatch.build.targets.wheel.shared-data] | ||
"etc/jupyter/jupyter_server_config.d" = "etc/jupyter/jupyter_server_config.d" | ||
|
||
[tool.hatch.build] | ||
include = [ | ||
"serve/", | ||
"etc/", | ||
"setup.py", | ||
"jupyter_server_config.py" | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from .application import Application | ||
|
||
|
||
# Sets server activation ID and the app itself, called from jupyter server --ServerApp.jpserver_extensions="{'simple_ext1': True}" | ||
def _jupyter_server_extension_points(): | ||
return [{"module": "serve.application", "app": Application}] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
"""Application cli main.""" | ||
from .application import main | ||
|
||
# Main entry point into program | ||
if __name__ == "__main__": | ||
main() |
51 changes: 51 additions & 0 deletions
51
jupyterlab_polus_render/render-server-ext/serve/application.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"""Jupyter server example application.""" | ||
import os | ||
|
||
from jupyter_server.extension.application import ExtensionApp, ExtensionAppJinjaMixin | ||
# Handles incoming requests and adds endpoints | ||
from .handlers import ( | ||
DefaultHandler, | ||
ErrorHandler, | ||
AuthFileHandler, | ||
) | ||
|
||
|
||
DEFAULT_STATIC_FILES_PATH = os.path.join(os.path.dirname(__file__), "static") | ||
DEFAULT_TEMPLATE_FILES_PATH = os.path.join(os.path.dirname(__file__), "templates") | ||
|
||
|
||
class Application(ExtensionAppJinjaMixin, ExtensionApp): | ||
"""A simple jupyter server application.""" | ||
# The name of the extension. | ||
name = "render" | ||
|
||
# The url that your extension will serve its homepage. | ||
default_url = "/render/default/" | ||
|
||
# Should your extension expose other server extensions when launched directly? | ||
load_other_extensions = True | ||
|
||
# Local path to static files directory. | ||
static_paths = [DEFAULT_STATIC_FILES_PATH] # type:ignore[assignment] | ||
|
||
# Local path to templates directory. | ||
template_paths = [DEFAULT_TEMPLATE_FILES_PATH] # type:ignore[assignment] | ||
|
||
# Init handlers and link endpoints to them | ||
def initialize_handlers(self): | ||
"""Initialize handlers.""" | ||
self.handlers.extend( | ||
[ | ||
(rf"/{self.name}/default/(.*)", DefaultHandler), | ||
(rf"/{self.name}/file/(.+)", AuthFileHandler, {"path":"/"}), | ||
(rf"/{self.name}/(.*)", ErrorHandler), | ||
] | ||
) | ||
|
||
|
||
def initialize_settings(self): | ||
"""Initialize settings.""" | ||
self.log.info(f"Config {self.config}") | ||
|
||
main = launch_new_instance = Application.launch_instance | ||
|
Oops, something went wrong.