diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c34d28b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +.DS_Store \ No newline at end of file diff --git a/Build Instructions.md b/Build Instructions.md deleted file mode 100644 index 92db5f2..0000000 --- a/Build Instructions.md +++ /dev/null @@ -1,20 +0,0 @@ -## Build Instructions -- cd to polus-render root directory. -- 'py -m build' -- 'py -m twine upload dist/*' or 'py -m twine upload --repository testpypi dist/*' -- Enter '__token__' as user and reference API keys for password - -## Adding a local build of Polus Render -- Remove all existing files in '~/polus-render/src/apps/render-ui'. -- Run 'npx nx build render-ui' in your Polus Render folder -- Tranfer generated files from '/Polus Render/dist/apps/render-ui' into '/polus-render/src/apps/render-ui'. - -## NOTE: -- For each upload, version number must be changed in pyproject.toml -- Add additional files to MANIFEST.in to bundle them with Pypi package - -**Git Repository**: https://github.com/jcaxle/polus-render - -**Pypi**: https://pypi.org/project/polus-render/ - -**Test Pypi**: https://test.pypi.org/project/polus-render/ diff --git a/LICENSE b/LICENSE index 335ea9d..63ee9f5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -Copyright (c) 2018 The Python Packaging Authority +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 diff --git a/README.md b/README.md index 8feef02..ff327db 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Polus Render -Render application is loaded in an iframe. The package allows pointing the iframe at: -* Render deployed to a server -* A Python server running on localhost and serving a production build of render, which has been bundled with this package +`polus-render` makes Polus Render available as a Pypi package for Jupyter Lab. + +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 -2. Specifying a local path will start a Python server on localhost. The URL to the dataset on localhost will be passed to the application in the iframe -3. 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) -
-Has the ability to work both local and remote versions of Jupyter Notebook. +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. + +Please note that [jupyterlab_polus_render](https://github.com/PolusAI/jupyterlab-extensions/tree/master/jupyterlab_polus_render) is the improved version of this package. -![image](https://github.com/jcaxle/polus-render/assets/145499292/2fcd525e-d97a-40fa-87f8-37981bd24be1) + # Requirements * Python 3.9+ @@ -41,36 +41,36 @@ polus-render | pyproject.toml // Pypi config | README | requirements.txt -└───src - | polus.py // Main file, contains render function used by user - | polus-render-wrapper.py // Unused file used for a scrapped project. Can be used as a reference for input sanitization - | zarr_file_server.py // Contains server only used for serving local build of Polus Render +└───polus + | polus_render.py // Main file, contains render function used by user + | render_server.py // Contains server only used for serving local build of Polus Render ├───apps - │ ├───render-ui // Build files of Polus Render + │ ├───render-ui // Static Polus Render files │ └───updog-render // Server used for serving files. ``` # Build Instructions -- Refer to [Build Instructions.md](https://github.com/jcaxle/polus-render/blob/0.0.4/Build%20Instructions.md) +- cd to `polus-render` root directory. +- `py -m build` +- `py -m twine upload dist/*` +- Enter `__token__` as user and reference API keys for password + +# Adding a static build of Polus Render +- Remove all existing files in `~/polus-render/polus/apps/render-ui`. +- Run `npx nx build render-ui` in your Polus Render folder. +- Tranfer generated files from `/Polus Render/dist/apps/render-ui` into `/polus-render/polus/apps/render-ui`. # Submodules - [Updog-Render](https://github.com/jcaxle/updog-render/tree/71b6b938452f63412eea8edf29b9ff10f4c243dd) -# Render: Local build vs online -polus-render is bundled with a build of Polus Render which supports additional functionality compared to the web version. Table -is accurate as of 10/4/2023. +# Static Render Features | Version | Zarr from URL/Path | TIF from URL/Path | Micro-JSON Support | Zarr/TIF Drag & Drop | Micro-JSON Drag & Drop | |----------------|---------------|---------------|----------------|-----------|-----| | Local | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: -| Online | :heavy_check_mark: | | | | # Drag & Drop Demo -![ezgif-4-7162ca42b5](https://github.com/jcaxle/polus-render/assets/145499292/7a59db1e-3128-4ee0-b9cc-ad1be7d3faee) + -# Local Jupyter Notebooks Demo ->TODO -# Remote Jupyter Notebooks Demo ->TODO # Sample usage ``` Python from polus.polus_render import render, nb_render @@ -79,30 +79,30 @@ from polus.polus_render import render, nb_render from urllib.parse import urlparse from pathlib import Path -# Embeds an IFrame of a local build of Polus Render into Jupyter Notebooks +# Embeds an IFrame of a static build of Polus Render into Jupyter Notebooks render() # Embeds an IFrame of Polus Render into Jupyter Notebooks render(use_local_render=False) -# Embeds an IFrame of a local build of Polus Render with an image file hosted at "https://viv-demo.storage.googleapis.com/LuCa-7color_Scan1/" +# 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(image_location=urlparse("https://viv-demo.storage.googleapis.com/LuCa-7color_Scan1/")) -# Embeds an IFrame of a local build of Polus Render with an image hosted locally at "C:\Users\JeffChen\OneDrive - Axle Informatics\Documents\zarr files\pyramid.zarr" +# Embeds an IFrame of a static build of Polus Render with an image hosted locally at "C:\Users\JeffChen\OneDrive - Axle Informatics\Documents\zarr files\pyramid.zarr" render(image_location=Path(r"C:\Users\JeffChen\OneDrive - Axle Informatics\Documents\zarr files\pyramid.zarr")) -# Embeds an IFrame of a local build of Polus Render with an image and overlay file that is hosted locally +# Embeds an IFrame of a static build of Polus Render with an image and overlay file that is hosted locally render(image_location=Path(r"C:\Users\JeffChen\OneDrive - Axle Informatics\Documents\zarr files\pyramid.zarr"), \ microjson_overlay_location=Path(r"C:\Users\JeffChen\OneDrive - Axle Informatics\Documents\overlay files\x00_y01_c1_segmentations.json")) -# Embeds an IFrame of a local build of Polus Render with an image and overlay file that is hosted online +# Embeds an IFrame of a static build of Polus Render with an image and overlay file that is hosted online render(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")) # Embeds an IFrame with a height of 1080 of a local build of Polus Render. render(height=1080) -# Embeds an IFrame into remote jupyter notebooks. Use this function with argument nbhub_url to specify your notebooks url which must have lab in its url +# Embeds an IFrame into remote JupyterLab notebook. Use this function with argument nbhub_url to specify your notebooks url which must have lab in its url nb_render(nbhub_url=urlparse("https://jh.scb-ncats.io/user/jeff.chen@axleinfo.com/user-namespaces/lab?"), image_location=Path("work/pyramid.zarr")) ``` @@ -121,8 +121,8 @@ def render(image_location:Union[ParseResult, PurePath] = "", microjson_overlay_l 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 - image_port (int): Port to run local zarr server on if used (default is 0 which is the 1st available port). - microjson_overlay_port (int): Port to run local json server on if used (default is 0 which is the 1st available port). + image_port (int): Port to run local image server if used (default is 0 which is the 1st available port). + microjson_overlay_port (int): Port to run local overlay server if used (default is 0 which is the 1st available port). use_local_render (bool): True to run local build of render with 1st available port, False to use render_url (default is True) render_url (str): URL which refers to Polus Render. Used when run_local_render is False. (default is https://render.ci.ncats.io/) Pre: zarr_port and json_port selected (if used) is not in use IF path given is Purepath @@ -149,18 +149,15 @@ def nb_render(nbhub_url:ParseResult,image_location:Union[ParseResult, PurePath] ``` # Implementation Details -- render() receives sanatized input. Check [polus-render-wrapper.py](https://github.com/jcaxle/polus-render/blob/dev-experimental/src/polus-render-wrapper.py) or [sample usage](https://github.com/jcaxle/polus-render/edit/dev-experimental/README.md#sample-usage) for examples on sanitizing input. -- render() builds up URL scheme fragments for render url, image url, and microjson url. -- If the image url and microjson url are file paths, serve the files on file servers pointing to either user specified port or a free port. -- If local render is used, build a server for it as well. -- At the end, combine render url fragments into a single url, insert it into an IFrame, and display it. -- Complete url string is returned not printed. -- For nb_render(), no servers are launched. Files are served from endpoints generated from the remote Jupyter Notebook's URL. The local build of render is served from the [polus-server-ext](https://github.com/jcaxle/polus-server-ext) instead of the bundled build files. Essentially, nb_render() runs serverless with the exception of the server that serves the remote Jupyter Notebooks itself. +- Render application is loaded in an IFrame. +- `render()` and `nb_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 in `nb_render()` 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). # Misc Implementation Details - Two type of servers are used. ->1. Python HTTPServer with CORS and OPTIONS functionality to serve RenderUI ->2. Modified UpDog Flask server to serve local files to RenderUI +>1. Python HTTPServer with CORS and OPTIONS functionality to serve Render. +>2. Modified UpDog Flask server to serve local files to Render. # Acknowledgements - UpDog: https://github.com/sc0tfree/updog diff --git a/images/drag-drop.gif b/images/drag-drop.gif new file mode 100644 index 0000000..b185441 Binary files /dev/null and b/images/drag-drop.gif differ diff --git a/images/home.png b/images/home.png new file mode 100644 index 0000000..36afa6d Binary files /dev/null and b/images/home.png differ diff --git a/polus/__pycache__/__init__.cpython-39.pyc b/polus/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index b62b326..0000000 Binary files a/polus/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/polus/__pycache__/polus.cpython-311.pyc b/polus/__pycache__/polus.cpython-311.pyc deleted file mode 100644 index f3dce9f..0000000 Binary files a/polus/__pycache__/polus.cpython-311.pyc and /dev/null differ diff --git a/polus/__pycache__/polus_render.cpython-39.pyc b/polus/__pycache__/polus_render.cpython-39.pyc deleted file mode 100644 index 25c10f3..0000000 Binary files a/polus/__pycache__/polus_render.cpython-39.pyc and /dev/null differ diff --git a/polus/__pycache__/render_server.cpython-39.pyc b/polus/__pycache__/render_server.cpython-39.pyc deleted file mode 100644 index 221781c..0000000 Binary files a/polus/__pycache__/render_server.cpython-39.pyc and /dev/null differ diff --git a/polus/__pycache__/zarr_file_server.cpython-311.pyc b/polus/__pycache__/zarr_file_server.cpython-311.pyc deleted file mode 100644 index 6c34d9f..0000000 Binary files a/polus/__pycache__/zarr_file_server.cpython-311.pyc and /dev/null differ diff --git a/polus/polus-render-wrapper.py b/polus/polus-render-wrapper.py deleted file mode 100644 index 40e7006..0000000 --- a/polus/polus-render-wrapper.py +++ /dev/null @@ -1,83 +0,0 @@ -# Adjust path to add polus-render -import contextlib -import sys -import os -from urllib.parse import ParseResult, urlparse -from pathlib import Path, PurePath -import argparse -from typing import Union -import re - -# Print blockers to ensure output is exactly what Node.js wants -def blockPrint(): - sys.stdout = open(os.devnull, 'w') - -def enablePrint(): - sys.stdout = sys.__stdout__ - -# Add polus to path -#script_dir = os.path.dirname( __file__ ) -#mymodule_dir = os.path.join( script_dir, 'polus-render', 'src' ) -#sys.path.append(mymodule_dir) -import polus - - - -def build_custom_render(image_location: Union[ParseResult,PurePath] = "", overlay_location:Union[ParseResult,PurePath] = ""): - """ - Call Polus.render and prints the str output. - - Args: - image_location (ParseResult | PurePath, optional): _description_. Defaults to "". - overlay_location (ParseResult | PurePath, optional): _description_. Defaults to "". - - Returns: - _type_: _description_ - """ - print(polus.render(image_location=image_location, microjson_overlay_location=overlay_location)) - -def convert_args_for_render(target:str)->Union[ParseResult,PurePath,str]: - """ - Return the target as either a PurePath or ParseResult - - Args: - target (str): string to convert - returns: if target is a URL, returns ParseResult, otherwise, return PurePath. If target is empty, return empty string - Pre: target is either url or path. - Post: If pre is not met, ParseResult of a url return is 100% guaranteed; however, PurePath of a path is not guaranteed - """ - if len(target) == 0: - return target - - url_regex = r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))" - result = re.findall(url_regex, target) - - if len(result) > 0: - return urlparse(target) - else: - return Path(target) -def main(): - """ - Entry point to the wrapper - """ - # Extract args - image_location = convert_args_for_render(r'{}'.format(args.image_location)) if args.image_location else "" - overlay_location = convert_args_for_render(r'{}'.format(args.overlay_location)) if args.overlay_location else "" - # Call render - with open(os.devnull, 'w') as devnull: - with contextlib.redirect_stdout(devnull): - render_url = polus.render(image_location=image_location, microjson_overlay_location=overlay_location, use_local_render= not args.use_online) - - # Output render url - print(render_url) - -if __name__ == "__main__": - # Initialize command line arguments - parser = argparse.ArgumentParser(prog="polis-render-wrapper", description="polus-render package wrapper for Node.JS child_process") - parser.add_argument('-i', '--image_location', action='store', default=None, dest="image_location", help="URL or File Path to Zarr/Tif file", required=False) - parser.add_argument('-o', '--overlay_location', action='store', default=None, dest="overlay_location", help="URL or File Path to MicroJSON file", required=False) - parser.add_argument('-t', '--use_online', action=argparse.BooleanOptionalAction, default=False, dest="use_online", help="Use online render", required=False) - args = parser.parse_args() - main() - - diff --git a/polus/x00_y01_p01_c1.ome.tif b/polus/x00_y01_p01_c1.ome.tif deleted file mode 100644 index 9d267c8..0000000 Binary files a/polus/x00_y01_p01_c1.ome.tif and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 823cfa8..cf536dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,11 +9,11 @@ namespaces = true [project] name = "polus-render" -version = "0.0.4.0.1.5" +version = "0.1.0" authors = [ { name="Jeff Chen", email="Jeff.Chen@axleinfo.com" }, ] -description = "Displays Polus Render dashboard in Jupyter Notebooks." +description = "Displays Polus Render dashboard in JupyterLabs." readme = "README.md" requires-python = ">=3.9" classifiers = [